Java Concurrency 08--Share data between multiple threads



Let's first look at a problem of sharing data between multiple threads:

Design four threads, two of which increase data by 1 each time, and the other two threads decrease data by 1 each time.

  Based on the idea of ​​task and thread separation: sharing data between multiple threads mainly focuses on two points: First, what is the task? How many tasks? Second, how many threads? Remember one thing: several tasks are not related to several threads! 100 threads can perform one task, or two tasks, three tasks... If there is only one task, it means that multiple threads perform one task. We only need to implement a Runnable interface, put the public data into Runnable, and put the task Put it in run() (tasks should be synchronized), and then open N threads to execute this task; if there are M tasks, then we create a new class dedicated to executing tasks, and put the public data into the class , you can use the task as a synchronization method in the class, then start N threads, throw a Runnable in each thread, and execute the method in the task class as required.

  From the point of view of the problem, it is obvious that the sharing of data between threads is involved. Four threads share one data and jointly operate one data. Let's put the above problem aside first, and slowly analyze some situations in which data is shared between multiple threads. Start with the simplest, and after the classification and analysis is completed, the above problem can be solved at that time.

1. Each thread performs the same task

  This is the simplest case, such as selling tickets. Several threads jointly operate the variable that records the number of votes, and the task is to reduce it by one. In response to this situation, we only need to write a class to implement the Runnable interface, reduce the ticket by one in the run() method, and then throw the Runnalbe to multiple threads to execute, naturally they operate on the same data . Take a look at the code:

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--;
                }
            }
//      }
    }
}
  
  
  • 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

  This is well understood and easy to implement, four threads sold 4 tickets. The running result is:

Thread-0: 100
Thread-3: 99
Thread-2: 98
Thread-1: 97

2. Each thread performs a different task

  As described in the above topic, two threads execute data increment and two threads execute data decrement. In this case, we have to implement two Runnables, because it is obvious that there are two different tasks, one task executes data increase, and the other task executes data decrease. In order to facilitate maintenance, you can put the two task methods in one class, and then put the data in this class, and then pass it to different Runnables to complete the data sharing. as follows:

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);
    }
}
  
  
  • 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

  Take a look at the printout:

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

  The advantage of writing in this way is that the two task methods can be directly synchronized on the method name. The benefits of this mode have been mentioned in the previous blog post, and the encapsulation is good.
  At this point, I can finally realize the separation of tasks and threads. This kind of thinking can be regarded as a kind of object-oriented. I didn't feel much when I learned it before. Now the more I summarize it, the clearer the thinking.



Let's first look at a problem of sharing data between multiple threads:

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326877517&siteId=291194637