Java如何實現多個線程之間共享數據

實現多個線程之間共享數據

一、 如果每個線程執行的代碼相同

可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如:賣票系統

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

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 對象中存放那個共享數據(賣票系統可以這樣做)。

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 對象,每個線程對共享數據操作的方法也分配到這個對象中,這樣容易實現針對該數據進行的各個操作的互斥通信。

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 對象調用外部類的這些方法。

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 作為成員內部類或局部內部類。

 public class MultiThreadShareData {
   public static void main(String[] args) { 
       ShareData data = new ShareData(); 
       new Thread(()->{
           for(int i=0;i<100;i++){
               data.increment();
           }
       }).start();
 
       new Thread(()->{
           for (int j=0;j<100;j++) {
               data.decrement();
           }
       }).start();
   } 
}
 
/**
封裝共享數據的對象
*/
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;
   }
}

總之,要同步互斥的幾段代碼最好放在幾個獨立的方法中,這些方法再放入一個類中,這樣比較容易實現它們之間的同步互斥和通信。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: