Three methods to solve thread safety problems in Java: synchronization code block, synchronization method, lock


Preface

We first use the learned knowledge of multi-threading to simulate the movie ticket sales process, leading to thread safety issues.

 /*
    需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口
    请设计一个程序模拟该电影院售票过程。
    使用Thread类实现
    分析:   1.三个窗口相当于三个线程
            2.100张票属于共享资源
     */
  • Code 1: Multi-threaded implementation method 1 implementation
public class 案例演示1 {
    
    
    public static void main(String[] args) {
    
    
        CellThread th1 = new CellThread("窗口1");
        CellThread th2 = new CellThread("窗口2");
        CellThread th3 = new CellThread("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}
class CellThread extends Thread{
    
    
    //设置票为静态变量,即共享资源
    static int piao = 100;
    //提供无参构造
    public CellThread() {
    
    
    }
    //创建有参构造,不再调用setName()方法给线程起名字
    public CellThread(String name) {
    
    
        super(name);
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            if(piao>0){
    
    
                System.out.println(Thread.currentThread().getName()+"正在售卖"+(piao--)+"张票");
            }
        }
    }
}
  • Code 2: Implementation using multithreading method 2
public class 案例演示2 {
    
    
    public static void main(String[] args) {
    
    
        Myrunable myrunable = new Myrunable();
        //我们只创建了一个任务,所以是共同售卖100张票。
        Thread th1 = new Thread(myrunable, "窗口1");
        Thread th2 = new Thread(myrunable, "窗口2");
        Thread th3 = new Thread(myrunable, "窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}
class Myrunable implements Runnable{
    
    
    static int piao=1000;
    @Override
    public void run() {
    
    
        while (true) {
    
    
            if (piao > 0) {
    
    
                //模拟了一下网络延迟,发现出现不合理的数据,即出现了线程安全问题。
                //1.出现了0票和负数票,是因为线程并发执行导致的
                //2,出现了重复票,是因为原子性所导致的。(自行科普)
                try {
    
    
                    Thread.sleep(50);
                } catch (InterruptedException e) {
    
    
                }
                System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
            }
        }
    }
}

The above code has thread safety issues

  • Conditions for thread safety issues:
  1. Is a multithreaded environment
  2. Shared resources exist among multiple threads
  3. There are multiple statements operating on shared resources, and operations on shared resources are not atomic operations

1. Synchronize code blocks to solve thread safety issues

  • Use the format of the synchronization code block:
    /*
    我们使用同步代码块,将可能出现线程安全问题的代码包裹起来。
    synchronized (锁对象){
        需要同步的代码
    }

		锁对象:可以是java中的任意一个对象,常new Object()。注意不可以在括号内new对象,这样每个线程持有的不是同一把锁,没有效果。需要将该对象定义为静态成员变量,被线程共享。
		需要同步的代码:可能出现线程安全的问题。(千万不可以出现死循环,这样线程会出现阻塞)
    */
public class 使用同步代码块解决线程安全问题 {
    
    
    public static void main(String[] args) {
    
    
        Myrunable1 myrunable = new Myrunable1();
        Thread th1 = new Thread(myrunable, "窗口1");
        Thread th2 = new Thread(myrunable, "窗口2");
        Thread th3 = new Thread(myrunable, "窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}
class Myrunable1 implements Runnable {
    
    
    static int piao = 100;
    //注意:多个线程要使用同一把锁
    static Object obj=new Object();
 
    @Override
    public void run() {
    
    
        while (true) {
    
    
            //同步代码块,包裹可能出现线程安全问题的代码块
            //锁对象:可以是java中的任意一个对象,常new Object()
            synchronized (obj){
    
    
                if (piao > 0) {
    
    
                    try {
    
    
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
    
    
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
                }
            }
            }
    }
}
/*
为什么加了同步代码块就能解决线程安全问题?
    某个线程一旦抢占cpu时间片,进入同步代码块,就会持有该锁。
    其他线程没有该锁,只能等待,无法并发执行。
    当持有锁的线程出了同步代码块,就会释放锁,然后所有线程再次争抢时间片。
    但是加锁,数据安全,效率会降低。
 */

Second, the synchronization method solves thread safety issues

  • We can extract thread-safe code into a method, and add synchronized to the method to turn the method into a synchronized method, so there is no need to set a lock object.
public class 同步方法解决线程安全问题 {
    
    
    public static void main(String[] args) {
    
    
        Myrunable2 myrunable = new Myrunable2();
        Thread th1 = new Thread(myrunable, "窗口1");
        Thread th2 = new Thread(myrunable, "窗口2");
        Thread th3 = new Thread(myrunable, "窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

class Myrunable2 implements Runnable {
    
    
    static int piao = 100;
    @Override
    public void run() {
    
    
        //1.将可能出现线程安全问题的代码抽成方法
            maipiao();
    }

       //2.在方法上加上synchronized,将该方法变成个同步方法
    public synchronized void maipiao(){
    
    
        while (true){
    
    
            if (piao > 0) {
    
    
                try {
    
    
                    Thread.sleep(50);
                } catch (InterruptedException e) {
    
    
                }
                System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
            }
        }
    }
}
/*
我们单独使用同步代码块或同步方法都可以解决线程安全问题,
但是我们混合使用同步代码块和同步方法时候,可能不能解决线程安全问题。
那么是因为同步代码块和同步方法使用的不是同一把锁。
    同步方法使用的默认锁对象是this(当将同步方法定义成静态方法时,锁对象是当前类对象的字节码对象)
    同步代码块使用的锁对象是任意java对象
解决方法:将同步代码块的锁对象与同步方法保持一致。
 */
  • Note the lock object
  1. The lock object of the synchronization code block is any java object
  2. The lock object of the synchronization method is this
  3. The lock object of the static synchronization method is the current bytecode object
    Do not mix the synchronization code block and the synchronization method. If you use it, keep the lock object of the synchronization code block consistent with the lock object of the synchronization method .

Three, Lock locks solve thread safety issues

  • After JDK5, a new lock object Lock is provided. The Lock implementation provides a wider range of locking operations than the use of synchronized methods and statements.
  • ReentrantLock is an implementation class of its interface
  • void lock() add lock void unlock() release lock
  • format:
/*
    加锁
    lock.lock();
            try{
                可能出现线程安全的代码
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //释放锁
                lock.unlock();
            }

 */
public class jdk15之后lock锁 {
    
    
    public static void main(String[] args) {
    
    
        Myrunable3 myrunable = new Myrunable3();
        Thread th1 = new Thread(myrunable, "窗口1");
        Thread th2 = new Thread(myrunable, "窗口2");
        Thread th3 = new Thread(myrunable, "窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

class Myrunable3 implements Runnable {
    
    
    static int piao = 100;
    //1.创建lock实现类的一个对象
    static Lock lock =new ReentrantLock();
    @Override
    public void run() {
    
    
        while (true) {
    
    
            //2.加锁
            lock.lock();
            try{
    
    
                if (piao > 0) {
    
    
                    try {
    
    
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
    
    
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
                }
            }catch (Exception e){
    
    
                e.printStackTrace();
            }finally {
    
    
                //3.释放锁
                lock.unlock();
            }


        }
    }
}

Guess you like

Origin blog.csdn.net/m0_46988935/article/details/112864455