java multithreading (3) thread synchronization and communication

Keywords: Java less JDK jvm

1 Thread Synchronization

Thread synchronization refers to collaboration, assistance and cooperation, refers to the collaboration of multiple threads to access critical resources.
Why synchronize threads?
The main problem is thread visibility.
How to realize thread synchronization in java?
Generally speaking, there are several ways to synchronize java threads

1. Synchronization through synchronization
There are two main ways, synchronization block and synchronization method.
Synchronization block:

    public void saveMoney2(int money){
        synchronized (this){
            account += money;
        }
    }

Here this refers to the object calling the method currently, so the lock is the lock of the current object and can be replaced by other objects, which means that the lock is the lock of other objects.

Synchronization method:

public synchronized void saveMoney(int money){
        account += money;
        System.out.println(Thread.currentThread().getName()+" Balance:<<----- " + getAccount());
}

A lock in a synchronization method represents a lock of the current object, that is, a lock that calls the method.

Let's look at a complete example of thread synchronization

public class Bank {
    private int account;

    public int getAccount() {
        return account;
    }

//    public  void saveMoney(int money){
//        account += money;
//    }

    public synchronized void saveMoney(int money){
        account += money;
    }

    public void saveMoney2(int money){
        synchronized (this){
            account += money;
        }
    }

//    public void getMoney(int money){
//        account -= money;
//    }

    public synchronized void getMoney(int money){
        account -= money;
    }






public class SaveTask implements Runnable {
    private Bank bank;

    public SaveTask(Bank bank){
        this.bank = bank;
    }

    @Override
    public void run() {
        while (true){
            bank.saveMoney(20);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}


/**
 * Created by qiyei2015 on 2017/2/11.
 */
public class TakeTask implements Runnable {

    private Bank bank;

    public TakeTask(Bank bank){
        this.bank = bank;
    }

    @Override
    public void run() {
        while (true){
            bank.getMoney(30);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//Test code:
private static void testSynchronzied(){
        Bank bank = new Bank();
        SaveTask saveTask = new SaveTask(bank);
        TakeTask takeTask = new TakeTask(bank);

        for (int i = 0 ;i < 10;i++){
            new Thread(saveTask,"Savings threads" + (i+1)).start();
            new Thread(takeTask,"Money withdrawal thread"+ (i+1)).start();

        }

    }

Program output:

2-Thread Communication

Communication here mainly refers to the wake-up of threads, such as one thread entering blocking state and another thread waking it up. wait() and notify() are mainly used. notifyAll()

wait(): The method in the Object class, which is called in the thread, will enter the blocking waiting state and release the synchronization lock, and the thread will enter the waiting pool. So you need to call it in a synchronization block or in a synchronization method
Notify (): A method in the Object class that wakes up a thread in the same lock waiting pool, and the specific thread that wakes up is implemented by a virtual machine.
NotfyAll (); Method in Object class, wakes up all methods in the same lock waiting pool.

Then in the example above, we find that our balance will become negative, which is certainly not in line with the actual situation. So we need to modify our program so that the balance can't be negative. Here we will use our knowledge of thread communication.

Modify the withdrawal code, only the balance after withdrawal is greater than 200. Only then can we withdraw money, otherwise let the withdrawing thread wait here.

    public synchronized void getMoney(int money){
        //If less than 200, wait for the save thread
        if ((account - money) < 200){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            account -= money;
            System.out.println(Thread.currentThread().getName()+" Balance:------>> " + getAccount());
        }
    }

Modify the threads to show that you can withdraw money when the balance is greater than 200. So we can keep our balance always above 200.

    public synchronized void saveMoney(int money){
        account += money;
        //Wake up all threads after saving
        if (account > 200){
            notifyAll();
        }
        System.out.println(Thread.currentThread().getName()+" Balance:<<----- " + getAccount());
    }

Others remain unchanged, running code, program output:

It can be found that the balance will no longer be less than 200, indicating that our effect has been achieved.

3 threads join()

Understanding thread join s is a difficult point. Let's look at JDK's explanation.
public final void join()
throws InterruptedException
Waits for this thread to die.
this here refers to the thread object that calls join(). Because join() is an instance method. this means waiting for the call to join() thread object to end. For example, t.join() is used to block threads that call join() methods until the end of t run.

Note: General t.join() is used for calling in the main thread. Used to organize the running of multiple threads in a certain order
Example: Assuming there are 1,2,3,4 threads, if I want them to run in 3,1,2,4 order now, I can use join.

/**
 * Created by qiyei2015 on 2017/2/6.
 */
public class MyThread extends Thread {

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 5){
            i++;
            String name = Thread.currentThread().getName();
            System.out.println(name + " Function....." + i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//Test code:
    private static void testJoin(){
        MyThread thread1 = new MyThread("Thread 1");
        MyThread thread2 = new MyThread("Thread 2");
        MyThread thread3 = new MyThread("Thread 3");
        MyThread thread4 = new MyThread("Thread 4");

        try {
            thread3.start();
            thread3.join();

            thread1.start();
            thread1.join();

            thread2.start();
            thread2.join();

            thread4.start();
            thread4.join();

            System.out.println("Complete run of threads");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

The results are as follows:

Okay, that's all for this section. The next section will talk about JVM memory model and thread visibility.

Posted by lnenad on Tue, 26 Mar 2019 20:45:29 -0700