Multithreading in Java (4) Using synchronized locks to synchronize threads

This chapter mainly introduces how to synchronize threads and how to communicate among threads.

Why Thread Synchronization

Let's first look at the results of this code:

package com.Dan;

public class TestRunnable {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        Thread t1 = new Thread( myThread, "1 No. ticket window");
        Thread t2 = new Thread( myThread, "2 No. ticket window");
        Thread t3 = new Thread( myThread, "3 No. ticket window");
        t1.start();
        t2.start();
        t3.start();
    }
}
class MyThread implements Runnable{
    private int ticket = 90; // 90 tickets
    private String ticketWindowName; // ticket window

    @Override
    public void run(){
        for(int i = 0;i < 100; i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(this.ticket>0){
                System.out.println(Thread.currentThread().getName() + "Selling tickets---->" + (this.ticket--));
            }
        }
    }
}
// Ticket Selling Window No. 1 - > 90
// Ticket Selling Window No. 3 - > 88
// No. 2 Ticket Window - > 89
// Ticket Selling at No. 2 Ticket Window - > 87
// Ticket Selling Window No. 1 - > 87
// Ticket Selling at No. 3 Ticket Window - > 87
// No. 2 Ticket Window - > 86

// 87 The ticket was sold once by three windows. Think about that picture. There are three people sitting in one seat on the train.

As mentioned in the previous multi-threaded blog, JVM adopts preemptive scheduling model. When a thread sleep, other threads will preempt CPU resources. If it happens in a database, it's dirty reading. synchronized lock is used to solve this problem. The thread synchronization mechanism of multithreading is actually controlled by the concept of lock.

The first way: synchronized keyword modifier method

    // Modifying run Method
    @Override
    public synchronized void run(){
        for(int i = 0;i < 100; i++){
            if(this.ticket>0){
                System.out.println(Thread.currentThread().getName() + "Selling tickets---->" + (this.ticket--));
                notifyAll();
            }
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    // Ticket Selling Window No. 1 - > 90
    // Ticket Sales at Ticket Window No. 3 - > 89
    // No. 2 Ticket Window - > 88
    // Ticket Selling at No. 3 Ticket Window - > 87
    // Ticket Selling Window No. 1 - > 86
    // No. 2 Ticket Window - > 85
    // Ticket Selling at No. 3 Ticket Window - > 84
    // Ticket Selling Window No. 1 - > 83

    // Reference multithreading for notify and wait usage (3)

The second way: synchronized keyword modifies code blocks

    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (this) {
                if (this.ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "Selling tickets---->" + (this.ticket--));
                    notifyAll();
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    // Ticket Selling Window No. 1 - > 90
    // No. 2 Ticket Window - > 89
    // Ticket Selling Window No. 1 - > 88
    // Ticket Selling at No. 3 Ticket Window - > 87
    // Ticket Selling Window No. 1 - > 86
    // No. 2 Ticket Window - > 85
    // Ticket Selling at No. 3 Ticket Window - > 84
    // Ticket Selling Window No. 1 - > 83

The case of multiple objects with multiple locks

Three threads run at the same time. Each thread prints only one letter, and alternately prints ABCABC.

package com.Dan;

class MyThread2 implements Runnable {

    private Object prev;
    private Object self;

    public MyThread2(Object prev, Object self) {
        this.prev = prev;
        this.self = self;
    }

    @Override
    public void run() {
        int ticket = 8; // Print 8 times
        while (ticket > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.print(Thread.currentThread().getName());
                    ticket--;
                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {

        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        MyThread2 myThread1 = new MyThread2(c, a);
        MyThread2 myThread2 = new MyThread2(a, b);
        MyThread2 myThread3 = new MyThread2(b, c);

        new Thread(myThread1, "A").start();
        Thread.sleep(100);
        new Thread(myThread2, "B").start();
        Thread.sleep(100);
        new Thread(myThread3, "C").start();
        Thread.sleep(100);
    }
}
// ABCABCABCABCABCABCABCABC

Prev stands for the former object and self stands for itself. The thread holds the lock of the previous object and the lock of the object to be printed this time, performs printing, and then wakes up a thread waiting for the lock of the current object (the remaining thread) and lets it get the lock of the object. The prev.wait() method lets the thread enter a waiting state, lets the thread sleep, automatically releases the object lock it occupies, and waits for notify. This cycle prints ABC eight times.

To sum up

  1. When multiple threads access the same object, only one thread can acquire the lock of the object, and multiple objects need the lock of multiple objects.
  2. Which thread executes the method with synchronized keyword, which thread holds the lock of the object to which the method belongs, and other threads have to wait for the lock to be released before they can preempt the resource to obtain the lock of the object.
  3. When synchronized modifies non-static methods, the lock is the object itself, that is, this.
  4. When synchronized modifies the static method, this cannot be used in the method, so it locks not this, but this class. Therefore, the static synchronized method is also equivalent to the global lock.
  5. Using synchronized keywords, we should try to narrow down the scope of code blocks. It is better to add synchronization on code blocks rather than synchronization on the whole method. Because the scope of your lock is large and the time is long, other threads will not get the corresponding resources.
  6. Threads A hold the lock of the object, and threads B can call asynchronous synchronized methods in the object asynchronously.

Finish it! (,) (,) (,) (,) (,)
The paper is always shallow, and I know nothing about it. —— Lu You
Question canal that clear so, for the source of living water. —— Zhu Jia

Welcome to reprint, reprint please indicate the source!
If there are any mistakes, or your opinions, please don't hesitate to teach!
If you like it, please give me a compliment.

Keywords: Windows jvm Database

Added by rbudj on Tue, 02 Jul 2019 21:35:33 +0300