java thread synchronization

Posted by artisticre on Fri, 14 Jan 2022 17:53:40 +0100

Thread synchronization

Concurrency: multiple threads access the same object. Example: tens of thousands of people rob 100 tickets at the same time

When dealing with multithreading, multiple threads access the same object, and some threads also want to modify the object. Thread synchronization is required at this time,

Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it for the next thread

Thread synchronization: queue + lock (synchronized)

Problems with thread synchronization (loss of performance):

  • Holding a lock by one thread will cause all other threads that need the lock to hang
  • In multi-threaded competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems
  • If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems
package com.yuanyu.syn;

//Unsafe ticket buying
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"Xiao Hong").start();
        new Thread(buyTicket,"Xiao Hei").start();
        new Thread(buyTicket,"Xiaobai").start();
    }
}

class BuyTicket implements Runnable{

    private int ticketNums=10;
    private boolean flag=true;

    @Override
    public void run() {
        //Buy a ticket
        while (flag){
            buy();
        }
    }

    private void buy(){
        if (ticketNums<=0){
            flag=false;
            return;
        }

        //Analog delay
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"Yes"+ticketNums--);
    }
}

Program running results:

Each thread interacts in its own working memory. Improper memory control will cause data inconsistency

package com.yuanyu.syn;

//Unsafe withdrawal
//Two people go to the bank to withdraw money and open an account
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"Small Treasury");

        Drawing d1 = new Drawing(account,50,"I");
        Drawing d2 = new Drawing(account,100,"You");

        d1.start();
        d2.start();

    }
}

class Account{
    int money; //balance
    String name; //Card name

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//Bank: simulated withdrawal
class Drawing extends Thread{
    Account account; //account
    int drawingMoney; //Amount withdrawn
    int nowMoney; //Money in hand

    @Override
    public void run() {
        if (account.money-drawingMoney<0){
            System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw");
            return;
        }


        account.money=account.money-drawingMoney; //Account balance
        nowMoney=nowMoney+drawingMoney; //Money in hand

        System.out.println(account.name+"The balance is:"+account.money);
        System.out.println(this.getName()+"The money in hand is:"+nowMoney);                     				//this.getName()=Thread.currentThread().getName()
    }

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;



    }


}

Program running results:

You don't have enough money to withdraw
Small Treasury balance: 50
The money in my hand is: 50

Thread.sleep() delay can amplify the occurrence of the problem

package com.yuanyu.syn;

import java.util.ArrayList;

//Thread unsafe collection
public class UnsafeTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            } ).start();
        }

        //Delay can amplify the occurrence of problems
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) { //1000
//            e.printStackTrace();
//        }
        System.out.println(list.size()); //994
    }
}

The solution of Concurrency: thread synchronization

Access to "objects" is controlled through the synchronized keyword. Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock until the method returns, and the lock will not be released until the method returns. The blocked thread can obtain the lock and continue to execute

[defect]: if a large method is declared synchronized, the efficiency will be affected

Only the contents that need to be modified in the method need to be locked. Too many locks will waste resources

package com.yuanyu.syn;

//Buy a ticket safely
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"Xiao Hong").start();
        new Thread(buyTicket,"Xiao Hei").start();
        new Thread(buyTicket,"Xiaobai").start();
    }
}

class BuyTicket implements Runnable{

    private int ticketNums=10;
    private boolean flag=true;

    @Override
    public void run() {
        //Buy a ticket
        while (flag){
            buy();
        }
    }

    //The synchronized synchronization method locks this
    private synchronized void buy(){
        if (ticketNums<=0){
            flag=false;
            return;
        }

        //Analog delay
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"Yes"+ticketNums--);
    }
}

Program running results:

package com.yuanyu.syn;

//Safe withdrawal
//Two people go to the bank to withdraw money and open an account
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"Small Treasury");

        Drawing d1 = new Drawing(account,50,"I");
        Drawing d2 = new Drawing(account,100,"You");

        d1.start();
        d2.start();

    }
}

class Account{
    int money; //balance
    String name; //Card name

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//Bank: simulated withdrawal
class Drawing extends Thread{
    Account account; //account
    int drawingMoney; //Amount withdrawn
    int nowMoney; //Money in hand

    @Override
    public synchronized void run() {
        synchronized (account) {
            if (account.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw");
                return;
            }


            account.money=account.money-drawingMoney; //Account balance
            nowMoney=nowMoney+drawingMoney; //Money in hand

            System.out.println(account.name+"The balance is:"+account.money);
            System.out.println(this.getName()+"The money in hand is:"+nowMoney);       							//this.getName()=Thread.currentThread().getName()
        }
    }

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }
}

The object of the lock must be a variable, and it needs to be added, deleted and modified

The above code example changes not the bank but the account, so synchronized should lock the account

package com.yuanyu.syn;

import java.util.ArrayList;

//Thread safe collection
public class UnsafeTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            } ).start();
        }

        //Delay can amplify the occurrence of problems
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size()); 
    }
}

10000

synchronized(Obj) {} block (Alt+Ctrl+T)

Obj calls it a synchronization monitor

Obj can be any object, but it is recommended to use shared resources as synchronization monitors

There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself or class

Synchronization monitor execution process

  1. The first thread accesses, locks the synchronization monitor, and executes the code in it
  2. The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
  3. After the first thread is accessed, lock the synchronization monitor
  4. The second thread accesses, finds that the synchronization monitor has no lock, and then locks the access