Study Notes [Multithreading - Section 4: Simple Foundations and Security Issues of synchronized]

thread synchronization

Thread asynchronous model
Each thread executes its own and has nothing to do with each other. For example, there are two threads, one is for the younger brother to eat, and the other is for the elder sister to watch TV. Neither one affects the other.

thread synchronization model
In my understanding, there is some kind of waiting relationship between threads. When one thread executes, it must wait for the end of the other. For example, there are two threads, one shares the object dish, and there are two threads, the elder sister and the younger brother. The younger brother needs to wait when the elder sister is serving food, and the younger brother needs to wait when serving food. In this case, the efficiency is low, and it needs to be executed in a queue, which involves the keyword synchronized.

thread safety issues

Conditions where security concerns exist
1. Multi-threaded concurrency
2. There is shared data
3. Both have modified behavior on shared objects

example
Suppose simply simulate a bank multi-threaded withdrawal, there is an Account user class, there are two instance variables of account number and balance, there is a withdrawal method, a parameter of the withdrawal amount is passed in, sleep for one second (simulate network delay), and then change Account balance; there is a withdrawal thread and an account instance variable; in the main method, two withdrawal threads are created, an account is created, and the two threads are respectively passed into this account to withdraw 5000.
Account class:

class Account{//User thread
    private String actNo;
    private double balance;
    public Account(String actNo, double balance) {
        this.actNo = actNo;
        this.balance = balance;
    }
    public String getActNo() {return actNo;}
    public void setActNo(String actNo) { this.actNo = actNo;}
    public double getBalance() { return balance;}
    public void setBalance(double balance) {this.balance = balance;}
    @Override
    public String toString() {
        return "Account{" +
                "actNo='" + actNo + '\'' +
                '}';
    }
    public synchronized void withDraw(double money){//Withdrawal method
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setBalance(getBalance()-money);
    }
}

Withdrawal thread:

class MyWithdrawThread extends Thread{//money withdrawal thread
    private Account act;
    public MyWithdrawThread(Account act) {
        this.act = act;
    }
    @Override
    public void run() {
        int money=5000;
        act.withDraw(money);
        System.out.println(Thread.currentThread().getName()+"withdraw money"+money+"Yuan Success! balance"+ act.getBalance()+"Yuan!");
    }
}

Main method:

public class WithdrawThreadTest {
    public static void main(String[] args) {
        Account account=new Account("act-01",10000);
        MyWithdrawThread t1=new MyWithdrawThread(account);
        MyWithdrawThread t2=new MyWithdrawThread(account);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

result:

As a result, both threads have withdrawn 5,000 from it, but the balance is still 5,000. This is because both threads read 10,000 when they read the balance, and they both changed the value and output. If it is really a bank, just The loss is lost. Of course, this is just a very simple simulation, and it can't be compared with the big guy...

This is the issue of thread safety. At this time, we can add synchronized to the money withdrawal method to lock it, so that another thread waits outside when one thread makes changes to the shared data.

public  void withDraw(double money){
        synchronized (this) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setBalance(getBalance() - money);
        }
    }


At this time, the output is correct.

Here, I use this, that is, only the object, this is not necessary, what thread to lock, just put a shared object of these threads on it.

Since instance variables are in the heap, static methods are in the method area, and local variables are in the stack, threads share heap memory and method areas, and each thread has a stack. So there is no thread safety problem with local variables.

Of course, you can also put synchronized on the instance method, then the default lock is this, this method is not flexible enough, thinking that there may be many operations that do not modify shared data, but are locked and inefficient; synchronized can also be used in static In terms of method, the lock is a class, and a class has only one lock.
Notice
It is best not to use synchronized in nesting, otherwise deadlock will occur accidentally, and it is not easy to debug.

Tags: Java Programming Multithreading Concurrent Programming thread

Posted by xzazx on Tue, 03 May 2022 07:27:00 +0300