Java multithreading study notes _ selling ticket case analysis

One, synchronization code block

Post the code first:

public class MyRunnable implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if(ticket == 0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }    
                ticket--;
                System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        MyRunnable mr1 = new MyRunnable();

        Thread t1 = new Thread(mr1);
        Thread t2 = new Thread(mr1);
        Thread t3 = new Thread(mr1);

        t1.setName("#窗口一#");
        t2.setName("##窗口二##");
        t3.setName("###窗口三###");

        t1.start();
        t2.start();
        t3.start();
    }
}

The code here is different from the previous blog post ( implementation of ticket selling case ), in order to elicit the content that needs to be explained.

The difference is that the judgment condition of the while loop is changed to true, which increases thread sleep;

Problem: During the execution of the above code, there will be duplicate votes and negative votes.

The reason is that during the sleep process, the control of thread A is given to other threads (for example, B). In this process, thread B modifies the shared data ticket, so when thread A prints, the ticket is already It's not that the ticket was changed, he changed.

And it is very likely that when the ticket is set to zero, the data value will be changed before the judgment is made (zero will terminate the loop), resulting in an infinite loop.

Change the if judgment condition to <=0, the infinite loop will not appear, but there are still duplicate votes and negative votes.

Therefore, if the shared data is not allowed to be manipulated when other threads are executing, then this situation will be resolved.

This requires the synchronized() method. Lock multiple codes that operate on shared data to realize synchronized code blocks.

synchronized(任意对象){
    操作共享数据的多条语句
}

By default, the lock is opened, as long as there is a thread to execute the code, it will be closed.

When the thread is executed, the lock will be automatically opened.

Advantages and disadvantages of synchronization:

  • Benefits: Solve the problem of multi-threaded data security.
  • Disadvantages: When there are many threads, all threads will judge the synchronization lock, which is very resource intensive and will virtually reduce the efficiency of program execution.

Two, the lock object is unique

The implementation method used in the above ticket selling case is to inherit the Runnable interface, and the same parameter is used between threads, so the lock is also shared.

When each thread has its own lock, there will still be the problem of duplicate votes and negative votes, that is, the thread becomes insecure. (When implementing multithreading by inheriting the Thread class)


public class MyThread extends Thread {
    private static int ticket = 100;
    private static Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized(obj){
                if(ticket <= 0){
                    break;
                }else {

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

                    ticket--;

                    System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
                }
            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("窗口一");
        t2.setName("窗口二");

        t1.start();
        t2.start();
    }
}

This is to set the ticket variable and Object object in the MyThread class to static, so that all MyThread class objects share a static variable.

Three, synchronization method

The lock object of the synchronization method is: this

The lock object of the synchronous static method is: class.class



public class MyRunnable implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {

        while (true) {
            if("窗口一".equals(Thread.currentThread().getName())){
                //同步方法
                boolean res = synchronizedMethod();
                if(res){
                    break;
                }
            }

            if("窗口二".equals(Thread.currentThread().getName())){
                //同步代码块
                synchronized(this){
                    if(ticket == 0){
                        break;
                    }else {

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        ticket--;
                        System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
                    }
                }
            }
        }

    }

    private synchronized boolean synchronizedMethod() {
        if (ticket == 0) {
            return true;
        } else {

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            ticket--;
            System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
            return false;
        }
    }
}


public class Demo {
    public static void main(String[] args) {
        MyRunnable mr1 = new MyRunnable();

        Thread t1 = new Thread(mr1);
        Thread t2 = new Thread(mr1);

        t1.setName("窗口一");
        t2.setName("窗口二");

        t1.start();
        t2.start();

    }
}

Note: The way this code implements multithreading is to inherit the Runnable interface. The parameters of the two threads are the same, and the run() method in the object mr1 of MyRunnable is executed, so the lock object this is the same.

Synchronous static method:

public class MyRunnable implements Runnable{
    //静态方法只能访问静态变量,所以要加上static进行修饰
    private static int ticket = 100;
    @Override
    public void run() {

        while (true) {
            if("窗口一".equals(Thread.currentThread().getName())){
                //同步方法
                boolean res = synchronizedMethod();
                if(res){
                    break;
                }
            }

            if("窗口二".equals(Thread.currentThread().getName())){
                //同步代码块
                //这里的对象需要进行修改
                synchronized(MyRunnable.class){
                    if(ticket == 0){
                        break;
                    }else {

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        ticket--;
                        System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
                    }
                }
            }
        }

    }

    private static synchronized boolean synchronizedMethod() {
        if (ticket == 0) {
            return true;
        } else {

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            ticket--;
            System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
            return false;
        }
    }
}

Four, Lock

Although we can understand the lock object problem of the synchronization code block and the synchronization method, we have not directly seen where the lock is added and where the lock is released. In order to express more clearly how to lock and release the lock, it will be provided after JDK5. A new lock object Lock

The Lock implementation provides a wider range of locking operations than can be obtained by using synchronized methods and statements. Lock provides methods for acquiring and releasing locks.

  • void lock(): Obtain a lock
  • void unlock(): release the lock

Lock is an interface and cannot be instantiated directly. Here, its implementation class ReentrantLock is used to instantiate it.

import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread {
    private static int ticket = 100;
//    private static Object obj = new Object();

    ReentrantLock rlock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            
//            synchronized(obj){
            try {
                rlock.lock();
                if (ticket <= 0) {
                    break;
                } else {

                    Thread.sleep(100);
                    ticket--;

                    System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rlock.unlock();
            }
//            }
        }
    }
}

Five, deadlock

The deadlock is caused by the nesting of locks. It is recommended not to write the nesting of locks to avoid deadlocks.

Guess you like

Origin blog.csdn.net/qq_43191910/article/details/114987823