How does Java share data among multiple threads

  • 2021-12-12 04:12:04
  • OfStack

Directory to share data between multiple threads. 1. If each thread executes the same code. 2. If each thread executes different code, discuss the way to share data between multiple threads. 1: Code 1 to Mode 2: Code not 1 to

Realize data sharing among multiple threads

1. If each thread executes the same code

You can use the same Runnable object, which Runnable object has the shared data, such as the ticket selling system


class Ticket implements Runnable{  
    private  int tick = 20;  
    Object obj = new Object();    
    public void run(){  
        while(true){  
            synchronized(obj){  
                if(tick>0){  
                    // Only try , because run It is a copy Runnable Interface's run, Interface's run Without throwing   
                    try{Thread.sleep(100);}catch(Exception e){}  // Use sleep Otherwise, every thread of execution will be occupied 
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
                }  
            }  
        }  
    }  
}  
  
class  TicketDemo  
{  
    public static void main(String[] args) {  
          
        // Only established 1 A Ticket Object, only in memory 1 A tick Member variable, so it is shared data   
        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();  
    }  
} 

Output result

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

2. If each thread executes different code

1. Specific implementation

Encapsulate the shared data in another 1 object, and then pass this object one by one to each Runnable object. The operation method of each thread on shared data is also assigned to that object to complete, so it is easy to realize mutual exclusion and communication of various operations for this data.

Idea: One class provides a synchronous method for data and data operation. In addition, two threads are defined to receive and operate data through a constructor, and thread objects are directly created in the main function to complete the operation (two internal classes can be realized, and data local variables are defined using final without using constructor to pass values)

For example, design four threads, two of which add 1 to j each time, and the other two reduce 1 to j each time


public class MultyThreadShareMethod1 {        
    public static void main(String[] args){        
        // Encapsulate data into 1 Objects,   
        ShareData2 data1 = new ShareData2();  
          
        // In runnable Directly passed in to operate in the constructor of   
        for(int i=0;i<2;i++){  
        new Thread(new MyRunnable1(data1)).start();  
        new Thread(new MyRunnable2(data1)).start();  
        }  
    }  
}  
 
// Classes that encapsulate shared data and methods that manipulate shared data   
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);  
    }  
}  
   
// Added threads, need to pass in 1 Shared data   
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();  
        }  
    }  
}  
  
// Reduced threads, need to pass in 1 Shared data   
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();  
        }  
    }  
}  

Output result

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

2. Summary of skills

It is better to put several pieces of code that want to synchronize and exclude each other in several independent methods, and these methods are put in the same class again, so it is easier to realize synchronization and exclusion or communication between them.

The extreme and simple way to define one static variable in any one class will be shared by all threads.

Discussion on the way of sharing data among multithreads

Mode 1: Code 1 to

If each thread executes the same code, you can use an Runnable object that holds that shared data (which the ticket selling system can do).


public class MultiThreadShareData {
    public static void main(String[] args) {
        MyShareData shareData=new MyShareData();
        // Put it into different threads 
        new Thread(shareData).start(); 
        new Thread(shareData).start(); 
    }
}
 
class MyShareData implements Runnable {
        //  Shared data 
        private int count = 100; 
        @Override
        public void run() {
            while (count > 0) {
                synchronized (this) {
                    if (count > 0) {
                        count--;
                        System.out.println(Thread.currentThread().getName() + "  Reduced 1 , count Remaining: " + count);
                    } 
                } 
            }
        }
    }

Mode 2: The code is not 1

If each thread executes different code, you need different Runnable objects:

a. The shared data is encapsulated in an object, and then this object is passed to each Runnable object one by one, and the methods of each thread's operation on the shared data are also assigned to this object, so that mutual exclusive communication of each operation on the data is easily realized.


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++){
            // Adding data 
            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++){
            // Reduce the data 
            this.data.decrement();
        }
    }
}
 
/**
  Encapsulate shared data into 1 Category 
*/
class ShareData{
    // Shared data 
    private int j=0; 
    public synchronized void increment(){
        this.j++;
        System.out.println(Thread.currentThread().getName()+":j Increased 1 Posterior j="+j);
    }
 
    public synchronized void decrement() {
        this.j--;
        System.out.println(Thread.currentThread().getName()+":j Reduced 1 Posterior j="+j);
    }
    public int getJ() {
        return j;
    }
}

b. The Runnable object is regarded as the inner class of a certain class, and the shared data is regarded as the member variable of this outer class. The operation methods of each thread to the shared data are also allocated to the outer class to realize the mutual exclusion and communication operation of the shared data. Each Runnable object as the inner class calls these methods of the outer class.


public class MultiThreadShareData { 
    private int shareData=0; 
    public static void main(String[] args) {
        MultiThreadShareData m=new MultiThreadShareData();
        // Initialization Runnable Object 
        MyRunnable1 myRunnable1 = m.new MyRunnable1();
        MyRunnable2 myRunnable2=m.new MyRunnable2(); 
        // Open thread 
        new Thread(myRunnable1).start(); 
        new Thread(myRunnable2).start();
    }
 
    private synchronized void increment(){
        this.shareData++;
        System.out.println(Thread.currentThread().getName()+":shareData Increased 1 Posterior shareData="+shareData);
    }
 
    private synchronized void decrement() {
        this.shareData--;
        System.out.println(Thread.currentThread().getName()+":shareData Reduced 1 Posterior shareData="+shareData);
    }
 
    /**
     *  As an inner class Runnable Object 
     */
    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. The combination of the above two methods: the shared data is encapsulated in an object, and the operation methods of each thread on the shared data are also assigned to the object, which is regarded as a member variable of an external class or a local variable in a method, and Runnable of each thread is regarded as a member internal class or a local internal class.


 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();
   } 
}
 
/**
 Objects that encapsulate shared data 
*/
class ShareData{
   // Shared data 
   private int j=0;
 
   /**
     Methods for manipulating shared data 
   */
   public synchronized void increment(){
       this.j++;
       System.out.println(Thread.currentThread().getName()+":j Increased 1 Posterior j="+j);
   }
 
   public synchronized void decrement() {
       this.j--;
       System.out.println(Thread.currentThread().getName()+":j Reduced 1 Posterior j="+j);
   }
 
   public int getJ() {
       return j;
   }
}

In a word, it is best to put several pieces of code that want to synchronize and exclude each other in several independent methods, and then put these methods into a class, so it is easier to realize synchronization and mutual exclusion and communication between them.


Related articles: