Java foundation -- different regional representations of thread safety (synchronization lock and monitor), thread blocking (sleep and wait), wake-up mode, lock (mutual repulsion lock), and fair lock

Keywords: Java

Fundamentals of Java (19)——

1, Thread safety (sync lock and monitor)

1. Primer -- thread unsafe

For example, introduce a security lock:
10 stores, selling 1000 computers at the same time. The code is 10 threads competing for a resource (the code is as follows):

This will cause a serious problem, that is, multiple threads will compete for a resource at the same time, and multiple threads will grab the resource at the same time:

This phenomenon is thread unsafe.

2. Thread safety issues

Thread safety: multiple threads access asynchronous critical resources at the same time, which may cause data confusion.
Asynchronous: let's visit together.

resolvent:

1,Synchronous lock: synchronized

1.1 Sync code block:
The code to be synchronized is placed in the synchronization code block. When adding synchronization lock and synchronization monitor to require multiple threads to access, the same code of the same object is used
 When a thread enters the same code block, other threads will wait for the thread entering the synchronous code block to execute before entering.

1.2 Synchronization method:
The method modified by the synchronization lock is the synchronization method, and the synchronization monitor is the method itself. When one thread calls the synchronization method, other threads will wait for the synchronization method
 Call after execution. The method is required to be static or use the same object

3. Synchronous lock (synchronous code block) solves thread safety

Sync code block:

synchronized ( You need to put a synchronization monitor here ) {
    
    }

The reason to put the synchronization monitor is that 10 threads are created here, each of which is independent. If you do not access the same object and access different objects, it is meaningless and synchronization cannot be realized. Synchronization can only be achieved if all threads access the same resource.

Generally, a critical resource is defined to act as a synchronization monitor:

Note: this synchronization monitor must be an object, not even if it is a static constant.

Final code:

public class Homework {
    public static void main(String[] args) {
        ExecutorService loop = Executors.newFixedThreadPool(10);    //  Open 10 threads in the thread pool
        Sell sell = new Sell();
        for (int i = 1; i < 11; i++) {
            loop.submit(sell);
        }
        loop.shutdown();
    }
}
class Sell implements Runnable{
    static int num = 1;     //  Exclusive space
    static final Object obj = new Object();

    @Override
    public void run() {
        while (num < 1001){
            //  Synchronized this is a synchronized lock. obj, this is a monitor
            synchronized (obj){
                if (num < 1001){
                    System.out.println("The first"+(Thread.currentThread().getId()-10)+"The alien shop sells the third"+num+"Computers");
                }
                num++;
            }
        }
        System.out.println("The first"+(Thread.currentThread().getId()-10)+"The alien shop has sold out");
    }
}

Note: the lock should be placed outside the if judgment statement. If you put it inside, it will overflow. Because there will be several threads coming in at the same time, one of which has reached 1000, and the threads queued later will continue to fetch resources, and then 1001 will occur (no more than 1000 + threads at most).

The test shows that the final effect is achieved, and there is no thread safety problem.

4. Synchronization lock (synchronization method) solves thread safety

a. Inheritance mode

You only need to add synchronized in front of the method return value, that is, the synchronized method. However, it should be noted that it cannot be directly added to the run method, and another method needs to be opened. However, this is not enough because the synchronization monitor is missing. The synchronization method monitor is the synchronization method itself. And it must be ensured that each thread calls the same method, so this method must be static in order to play the role of synchronization monitor.

code:

public class Demo01 {
    public static void main(String[] args) {
        for (int i = 1; i < 11; i++) {      //  Open 10 threads
            new MyThread("The first"+i+"home").start();
        }
    }
}

class MyThread extends Thread{
    static int computer = 1;
    private boolean flag = true;    //  Switch. Controls whether the cycle continues.

    public MyThread(String name){   //  Call the parameterized construction method of the parent class to give the thread a new name. Because the thread itself can call the getName () method.
        super(name);
    }

    @Override
    public void run() {     //  Override run method
        while (flag){       //  A while loop cannot be added to a synchronization method.
            flag = sell();
        }
    }

    //  The monitor of the synchronization method is the method itself, and the method is static
    public synchronized static boolean sell(){
        if (computer<1001){
            System.out.println(Thread.currentThread().getName()+"Alien store sales No"+computer+"Computers");
            computer++;
            return true;
        }else {
            System.out.println(Thread.currentThread().getName()+"The alien store has sold out");
            return false;
        }
    }

b. Implementation mode

The above is implemented by inheritance. If the interface is implemented, the following places should be changed:

There can be two parameters.

Illustration:

5. Review StringBuffer and StringBuilder

These two have been mentioned before. We can know which thread is safe by looking at the underlying code:

The conclusion is: StringBuffer is safe.

2, Method of getting thread into blocking

1. Understanding wait ()

As I learned earlier, sleep () is called directly through the Thread to block the Thread. wait () can also cause threads to get stuck and call through objects. wait() comes from the Object class.

2. Different behaviors of wait and sleep in synchronous code block

Test the different performance of wait and sleep in synchronous code blocks. If the thread is blocked, other threads can come in, indicating that the resources of the thread have been released; On the contrary, if it cannot come in, it means that the resources of this thread have not been released and other threads are still waiting.

a. sleep sleeps in the synchronization area

Test code:

class MyThread extends Thread{  //  Test whether hibernation releases resources
    public void run(){
        synchronized (Demo01.obj){
            System.out.println(Thread.currentThread().getName()+"thread  A Start sleep");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"thread  A End sleep");
        }
    }
}

b. wait waits in the synchronization area

class MyThread2 extends Thread{ // Test whether waiting releases resources
    public void run(){
        synchronized (Demo01.obj){
            System.out.println(Thread.currentThread().getName()+"thread  Z Start waiting");
            try {
                Demo01.obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"thread  Z End waiting");
        }
    }
}

result:

As you can see, other threads came in.

c. Conclusion - within the synchronization area

After testing, it is concluded that:
The thread enters the sleep state in the synchronization area, and the thread will not release resources.

When a thread enters a waiting state in the synchronization area, the thread will release resources.

d. wait() must be called through the object of the synchronization monitor


As you can see here, they are all the same object. If an object is also a static constant, an error will be reported:

Then run the results:

Wrong report.

3. Different behaviors of wait and sleep outside the synchronization area

a,sleep

Whether you can sleep outside the synchronization area.

In the past, when using sleep, there was no synchronization area, and there was no error at that time.

b,wait


As you can see, the report is wrong.

c. Conclusion

wait can only be used in the synchronization area.

4. Different objects cannot use wait() in synchronization

To be precise: the synchronization method of the same object uses the current object call.

Here are the methods that can be realized:

The thread is implemented by implementing the interface, and then the wait () method is called by this method, which can be called successfully. Other methods can't.

code:

class MyThread2 implements Runnable{	//	By implementing the interface. No way to inherit
    @Override
    public void run(){
        method();
    }

    private synchronized void method() {	//	There is no static here
        System.out.println(Thread.currentThread().getName()+"Start waiting");
        try {
            this.wait();	//	You can only use this here. If you call the synchronization monitor directly through a class, you can't.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"End waiting");
    }
}

5. Respective wake-up methods of sleep and wait

Direct conclusion:

Sleep: as the name suggests, sleep for a period of time will wake up by itself. So sleep wakes up automatically.

wait: notify() or notifyAll() is required to wake up. notifyAll() wakes up all threads; Notify is a thread.

a. Wake up a thread



result:

b. Wake up all threads

3, Lock - manual lock (mutex / reentrant lock)

1. First meet lock

Let's see what we can do first:

2. Use of lock

Object to instantiate:

Then lock the code block that needs to be locked:

In this way, the synchronization effect can be achieved after setting the code.

It is also an example of selling alien computers. Threads are created by inheritance:

public class Demo01 {
    public static void main(String[] args) {
        for (int i = 1; i < 11; i++) {      //  Open 10 threads
            new MyThread("The first"+i+"home").start();
        }
    }
}

class MyThread extends Thread{
    static int computer = 1;
    private static ReentrantLock lock = new ReentrantLock();    //  If threads are created through threads, in order to ensure that each thread uses the same lock, you need to add static here

    public MyThread(String name){   //  Call the parameterized construction method of the parent class to give the thread a new name. Because the thread itself can call the getName () method
        super(name);
    }

    @Override
    public void run() {     //  Override run method
        while (true){
            lock.lock();    //  Manual locking
            try {
                if (computer<1001){
                    System.out.println(Thread.currentThread().getName()+"Alien store sales No"+computer+"Computers");
                    computer++;
                }else {
                    System.out.println(Thread.currentThread().getName()+"The alien store has sold out");
                    break;
                }
            }finally {
                lock.unlock();  //  Now that you have to perform this unlocking in the end, put it in finally
            }
        }
    }

4, Fair lock

1. Understand fair and unfair locks

Here we need to explain the concepts of fair lock and unfair lock:

Fairness: each thread takes turns to get resources, and each thread has a share, which is a fair lock.

Unfair: there may be a thread taking multiple resources, which is an unfair lock. Threads with unfair locks fetch resources randomly

The following figure is unfair:

Computers are monopolized by the second store.

2. How fair locks are created

The fair lock is created in a simple way:

You only need to pass the parameter true when creating a lock, which is a fair lock.

3. Misunderstanding of fair lock

Sometimes a fair lock is created with 10 threads, but not every resource takes turns when taking resources. What's the matter?

First of all, this is really a fair lock. Each thread cannot take turns to get resources because some threads are too fast. When other threads have not been created, they immediately come back to get resources. You can manually increase the working time of threads, jump to an appropriate gap, and you can see that each thread takes turns to get resources. But the fact to admit is that it is indeed a fair lock here.

For example, several people take turns to buy milk tea. The first person left after buying milk tea, but the second person is still a little away from the milk tea shop. The first person immediately turned back to buy the second cup of milk tea. If you increase the distance for the first person to buy milk tea after buying milk tea, you can see the scene of buying milk tea in turn. That's the truth.

Posted by spetsacdc on Sat, 20 Nov 2021 02:20:40 -0800