阅读 42

Java如何实现多个线程之间共享数据

这篇文章主要介绍了Java如何实现多个线程之间共享数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

目录
  • 实现多个线程之间共享数据

    • 一、 如果每个线程执行的代码相同

    • 二、 如果每个线程执行的代码不同

  • 多线程之间共享数据的方式探讨

    • 方式一:代码一致

    • 方式二:代码不一致

实现多个线程之间共享数据

一、 如果每个线程执行的代码相同

可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Ticket implements Runnable{ 
    private  int tick = 20
    Object obj = new Object();   
    public void run(){ 
        while(true){ 
            synchronized(obj){ 
                if(tick>0){ 
                    //只能try,因为run是复写了Runnable接口的run,接口的run没有抛 
                    try{Thread.sleep(100);}catch(Exception e){}  //使用sleep不然执行每个线程都会占用完毕
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); 
                
            
        
    
   
class  TicketDemo 
    public static void main(String[] args) { 
           
        //只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据 
        Ticket t = new Ticket(); 
   
        Thread t1 = new Thread(t); 
        Thread t2 = new Thread(t); 
        Thread t3 = new Thread(t); 
        Thread t4 = new Thread(t); 
        t1.start(); 
        t2.start(); 
        t3.start(); 
        t4.start(); 
    
}

输出结果

Thread-0....sale : 20
Thread-1....sale : 19
 .......
Thread-3....sale : 2
Thread-3....sale : 1

二、 如果每个线程执行的代码不同

1、具体实现

将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,在主函数中直接创建线程对象,即可完成操作(可以实现两个内部类,不用构造方法传值,使用final定义data局部变量)

例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class MultyThreadShareMethod1 {       
    public static void main(String[] args){       
        //将数据封装到一个对象上, 
        ShareData2 data1 = new ShareData2(); 
           
        //在runnable的构造函数中直接传入去操作 
        for(int i=0;i<2;i++){ 
        new Thread(new MyRunnable1(data1)).start(); 
        new Thread(new MyRunnable2(data1)).start(); 
        
    
  
//封装共享数据和操作共享数据方法的类 
class ShareData2{ 
    private int j = 10
    public synchronized void increment() { 
        j++; 
        System.out.println(Thread.currentThread().getName()+" inc : "+j); 
    
    public synchronized void decrement() { 
        j--; 
        System.out.println(Thread.currentThread().getName()+" dec : "+j); 
    
    
//增加的线程,需要传入一个共享数据 
class MyRunnable1 implements Runnable {      
    private ShareData2 data; 
    public MyRunnable1(ShareData2 data) { 
        this.data = data; 
    
    @Override 
    public void run() { 
        for(int i=0;i<10;i++){ 
        data.increment(); 
        
    
   
//减少的线程,需要传入一个共享数据 
class MyRunnable2 implements Runnable {  
    private ShareData2 data; 
    public MyRunnable2(ShareData2 data) { 
        this.data = data; 
    
    @Override 
    public void run() { 
        for(int i=0;i<10;i++){ 
        data.decrement(); 
        
    
}

输出结果

Thread-0 inc : 11
...
Thread-1 dec : 10

2、 技巧总结

要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥或通信。


极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

多线程之间共享数据的方式探讨

方式一:代码一致

如果每个线程执行的代码相同,可以用一个 Runnable 对象,这个 Runnable 对象中存放那个共享数据(卖票系统可以这样做)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MultiThreadShareData {
    public static void main(String[] args) {
        MyShareData shareData=new MyShareData();
        //放入不同的线程中
        new Thread(shareData).start();
        new Thread(shareData).start();
    }
}
  
class MyShareData implements Runnable {
        // 共享的数据
        private int count = 100;
        @Override
        public void run() {
            while (count > 0) {
                synchronized (this) {
                    if (count > 0) {
                        count--;
                        System.out.println(Thread.currentThread().getName() + " 减了1,count还剩:" + count);
                    }
                }
            }
        }
    }

方式二:代码不一致

如果每个线程执行的代码不同时,就需要不同的 Runnable 对象:

a. 将共享数据封装在一个对象中,然后将这个对象逐一传递给各个 Runnable 对象,每个线程对共享数据操作的方法也分配到这个对象中,这样容易实现针对该数据进行的各个操作的互斥通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class MultiThreadShareData {
    private int shareData=0;
    public static void main(String[] args) {
        ShareData data = new ShareData();
        new Thread(new MyRunnable1(data)).start();
        new Thread(new MyRunnable2(data)).start();
    }
}
  
class MyRunnable1 implements Runnable{
    private ShareData data;
    public MyRunnable1(ShareData data){
        this.data=data;
    }
  
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            //对数据进行增加
            this.data.increment();
        }
    }
}
  
class MyRunnable2 implements Runnable{
    private ShareData data;
    public MyRunnable2(ShareData data){
        this.data=data;
    }
  
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            //对数据进行减少
            this.data.decrement();
        }
    }
}
  
/**
 将共享的数据封装成一个类
*/
class ShareData{
    //共享数据
    private int j=0;
    public synchronized void increment(){
        this.j++;
        System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
    }
  
    public synchronized void decrement() {
        this.j--;
        System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
    }
    public int getJ() {
        return j;
    }
}

b. 将 Runnable 对象作为某一个类的内部类,共享数据作为这个外部类的成员变量,每个线程对共享数据的操作方法也分配到外部类中,实现共享数据的互斥和通信操作,作为内部类的各个 Runnable 对象调用外部类的这些方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MultiThreadShareData {
    private int shareData=0;
    public static void main(String[] args) {
        MultiThreadShareData m=new MultiThreadShareData();
        //初始化Runnable对象
        MyRunnable1 myRunnable1 = m.new MyRunnable1();
        MyRunnable2 myRunnable2=m.new MyRunnable2();
        //开启线程
        new Thread(myRunnable1).start();
        new Thread(myRunnable2).start();
    }
  
    private synchronized void increment(){
        this.shareData++;
        System.out.println(Thread.currentThread().getName()+":shareData增加了1后shareData="+shareData);
    }
  
    private synchronized void decrement() {
        this.shareData--;
        System.out.println(Thread.currentThread().getName()+":shareData减少了1后shareData="+shareData);
    }
  
    /**
     * 作为内部类的Runnable对象
     */
    class MyRunnable1 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                increment();
            }
        }
    }
  
    class MyRunnable2 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                decrement();
            }
        }
    }
}

c. 以上两种方法的组合:将共享数据封装到一个对象中,每个线程对共享数据的操作方法也分配到对象中,对象作为外部类的成员变量或方法中的局部变量,每个线程的 Runnable 作为成员内部类或局部内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27