先看一个多线程间共享数据的问题:
设计四个线程, 其中两个线程每次对 data 增加 1, 另外两个线程每次对 data 减少 1.
从问题来看, 很明显涉及到了线程间通数据的共享, 四个线程共享一个 data, 共同操作一个 data. 我们先把上面这个问题放在一边, 慢慢分析多个线程之间共享数据的一些情况, 从最简单开始, 分类分析完了后, 到时候也好解决上面这个问题了.
1. 每个线程执行的任务相同
这是最简单的一种情况, 比如卖票, 几个线程共同操作记录票数的那个变量, 任务都是使它减一. 针对这种情况, 我们只需要写一个类实现 Runnable 接口即可, 在 run() 方法中对这个票进行减一, 然后将这个 Runnable 扔给多个线程去执行, 自然它们就操作同一个 data 了. 看一下代码:
- public class MultiThreadShareData {
- public static void main(String[] args) {
- ShareData task = new ShareData(); // 一个类实现了 Runnable 接口
- for(int i = 0; i <4; i ++) { // 四个线程来卖票
- new Thread(task).start();
- }
- }
- }
- class ShareData implements Runnable {
- private int data = 100;
- @Override
- public void run() { // 卖票, 每次一个线程进来, 先判断票数是否大于 0
- // while(data> 0) {
- synchronized(this) {
- if(data> 0) {
- System.out.println(Thread.currentThread().getName() + ":" + data);
- data--;
- }
- }
- // }
- }
- }
这很好理解, 也很容易实现, 四个线程卖了 4 张票. 运行结果为:
- Thread-0: 100
- Thread-3: 99
- Thread-2: 98
- Thread-1: 97
2. 每个线程执行不同的任务
就如上面那个题目所描述的, 两个线程执行 data 增, 两个线程执行 data 减. 针对这种情况, 我们要实现两个 Runnable 了, 因为很明显有两个不同的任务了, 一个任务执行 data 增, 另一个任务执行 data 减. 为了便于维护, 可以将两个任务方法放到一个类中, 然后将 data 也放在这个类中, 然后传到不同的 Runnabl e 中, 即可完成数据的共享. 如下:
- public class MultiThreadShareData {
- public static void main(String[] args) {
- ShareData task = new ShareData(); // 公共数据和任务放在 task 中
- for(int i = 0; i < 2; i ++) { // 开启两个线程增加 data
- new Thread(new Runnable() {
- @Override
- public void run() {
- task.increment();
- }
- }).start();
- }
- for(int i = 0; i < 2; i ++) { // 开启两个线程减少 data
- new Thread(new Runnable() {
- @Override
- public void run() {
- task.decrement();
- }
- }).start();
- }
- }
- }
- class ShareData /*implements Runnable*/ {
- private int data = 0;
- public synchronized void increment() { // 增加 data
- System.out.println(Thread.currentThread().getName() + ": before :" + data);
- data++;
- System.out.println(Thread.currentThread().getName() + ": after :" + data);
- }
- public synchronized void decrement() { // 减少 data
- System.out.println(Thread.currentThread().getName() + ": before :" + data);
- data--;
- System.out.println(Thread.currentThread().getName() + ": after :" + data);
- }
- }
看一下打印结果:
- Thread-0: before : 0
- Thread-0: after : 1
- Thread-1: before : 1
- Thread-1: after : 2
- Thread-2: before : 2
- Thread-2: after : 1
- Thread-3: before : 1
- Thread-3: after : 0
这样写的好处是两个任务方法可以直接在方法名上进行同步操作, 这种模式的好处在前面的博文中已经有说过了, 封装的好.
最后总结一下, 多个线程之间共享数据主要关注两点就行: 一是什么任务? 几个任务? 二是几个线程? 记住 一点: 几个任务和几个线程是没有关系的! 100 个线程可以执行一个任务, 也可以执行 2 个任务, 3 个任务......
如果只有一个任务, 那说明多个线程执行一个任务, 我们只要实现一个 Runnable 接口, 把公共 data 放进 Runnable, 把任务放进去 run() 中即可 (任务注意要同步), 然后开启 N 个线程去执行这个任务即可; 如果有 M 个任务, 那我们新建一个专门执行任务的类, 把公共的 data 放进类中, 把任务作为类中的同步方法即可, 然后开启 N 个线程, 每个线程中扔一个 Runnable, 按照要求执行任务类中的方法即可.
到这里, 读者应该能体会到任务和线程的分离了, 这种思想也算是面向对象的一种吧, 思路很清晰.
多个线程之间共享数据就总结这么多, 如有问题, 欢迎交流, 我们共同进步~
来源: https://www.cnblogs.com/eson15/p/10281027.html