Java Multithreading 07 - JUC Concurrency Package 03

1 The difference between synchronized, Lock and volatile thread locks

1.1 The difference between synchronized and volatile

usage:

  • The volatile keyword solves the problem of visibility of variables between multiple threads;
  • The synchronized keyword solves the synchronization of access to shared resources between multiple threads;
  • When multiple threads access volatile, the program will not block; when accessing synchronized modified methods or code blocks, blocking will occur;
  • volatile can guarantee the visibility of variables between multiple threads, but cannot guarantee atomicity; synchronized can guarantee the atomicity of data operations and can also indirectly guarantee the visibility of data, and will separate the data in private memory and public memory in the thread. Synchronize.

For example, when we use a credit card for consumption, if the bank freezes the card during consumption, the deduction should be rejected.

At this time, all threads need to be able to see the change in the card status, otherwise it will cause user losses.

To make this status visible to any thread, you need to use​volatile​​ to modify the variable.

Deductions are easier to understand. If the debit actions of the account are not locked, the same amount of money in the account can be consumed repeatedly, which will cause losses to the bank. After the lock is added, all deductions will be carried out serially here , and each consumption will be deducted one by one to avoid account overdraft or repeated payments.

scenes to be used:

  • The volatile keyword can only be used to modify variables;
  • The synchronized keyword can modify methods and code blocks.

1.2 The difference between synchronized and Lock

accomplish

Lock is an interface, ​synchronized​but a keyword in Java, implemented by the built-in language.

Exception handling mechanism

synchronized will automatically release the lock occupied by the thread when an exception occurs, so it will not cause deadlock;

When an exception occurs, if Lock does not actively release the lock through the unlock() method, it is likely to cause a deadlock. Therefore, when using Lock, you need to add a statement to manually release the lock in the finally block.

synchronized{
     语句块;
}

Lock lock = new ReentrantLock()
lock.lock();
lock.unLock();

lock() and unlock() must exist in pairs.

efficiency

Lock can improve the efficiency of read operations by multiple threads (read-write lock).

2 Thread read-write separation mechanism

​​ is​ReadWriteLock​ a read-write lock:

  • A pair of related locks "read lock" and "write lock" are maintained, one for read operations and the other for write operations.
  • Read lock is used for read-only operations. It is a shared lock and can be acquired by multiple threads at the same time.
  • The write lock is used for write operations. It is an exclusive lock. The write lock can only be acquired by one thread lock.
  • Read locks and write locks cannot exist at the same time. Read/read operations can be performed at the same time, but read/write and write/write operations cannot be performed at the same time.

2.1 Create account class

public class MyCount { 
    private String id;//Account 
    private int cash;//Account balance 

    public MyCount(String id, int cash) { 
        this.id = id; 
        this.cash = cash; 
    } 

    public String getId() { 
        return id ; 
    } 

    public void setId(String id) { 
        this.id = id; 
    } 

    //Read operation 
    public int getCash() { 
        System.out.println(Thread.currentThread().getName() + " getcash, cash=" + cash); 
        return cash; 
    } 

    //Write operation 
    public void setCash(int cash) { 
        System.out.println(Thread.currentThread().getName() + " setcash, cash=" + cash);
        this.cash = cash; 
    } 
}

2.2 Create user information class

A read-write lock is declared in the user information class,

and create a read lock in the read method,

Create a write lock in the write method,

All locks need to be closed manually after use.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class User {
    private String name;
    private MyCount myCount;
    //声明读写锁
    private ReadWriteLock readWriteLock;

    public User(String name, MyCount myCount) {
        this.name = name;
        this.myCount = myCount;
        this.readWriteLock = new ReentrantReadWriteLock();
    }

    //查询余额
    public void getCash(){
        new Thread(){
            @Override
            public void run() {
                //创建读取锁
                readWriteLock.readLock().lock();
                try {
                    System.out.println(Thread.currentThread().getName() + " getCash start");
                    myCount.getCash();
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " getCash end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动关闭锁
                    readWriteLock.readLock().unlock();
                }
            }
        }.start();
    }

    //设置余额
    public void setCash(final int cash){
        new Thread(){
            @Override
            public void run() {
                //Create a write lock 
                readWriteLock.writeLock().lock(); 
                try { 
                    System.out.println(Thread.currentThread().getName() + " setCash start"); 
                    myCount.setCash(cash); 
                    Thread.sleep (1000); 
                    System.out.println(Thread.currentThread().getName() + " setCash end"); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } finally { 
                    //Close the lock manually 
                    readWriteLock.writeLock ().unlock(); 
                } 
            }
        }.start();
    } 
}

2.3 Create test class

Create users and accounts in the test class, and use multi-threading to read and write account information.

public class Test { 

    public static void main(String[] args) { 
        //Create account 
        MyCount myCount = new MyCount("abcd12", 5000); 
        //Create user and specify account 
        User user = new User("Xiao Zhang" , myCount); 

        for (int i = 0; i < 3; i++) { 
            user.getCash(); 
            user.setCash((i + 1) * 1000); 
        } 
    } 
}

2.4 Output results

Thread-0 getCash start

Thread-0 getcash, cash=5000

Thread-2 getCash start

Thread-2 getcash, cash=5000

Thread-2 getCash end

Thread-0 getCash end

Thread-1 setCash start

Thread-1 setcash, cash=1000

Thread-1 setCash end

Thread-4 getCash start

Thread-4 getcash, cash=1000

Thread-4 getCash end

Thread-5 setCash start

Thread-5 setcash, cash=3000

Thread-5 setCash end

Thread-3 setCash start

Thread-3 setcash, cash=2000

Thread-3 setCash end

In the output results, you can see:

The read operation getCash is executed in Thread-0, but before it is completed, Thread-2 also enters the read operation at the same time, and they are executed in parallel ;

For the write operation setCash, all threads are executed from start to end, and no other threads enter in the middle. It is an exclusive execution .

Guess you like

Origin blog.csdn.net/QQ156881887/article/details/129100665