JUC系列之(七)Lock同步锁

Lock同步锁

用于解决多线程安全问题的方式:

  1. 同步代码块,synchronized实现,隐式锁

  2. 同步方法,synchronized实现,隐式锁

  3. 同步锁Lock:jdk 1.5以后

    注:是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法进行释放锁(一定要将unlock()放到finally中,保证一定会释放锁),更加灵活

示例:卖票

package com.atguigu.juc;

public class TestLock {
    public static void main(String[] args) {
        SellTicketWindow sellTicketWindow = new SellTicketWindow();

        new Thread(sellTicketWindow, "1号窗口").start();
        new Thread(sellTicketWindow, "2号窗口").start();
        new Thread(sellTicketWindow, "3号窗口").start();
    }
}

class SellTicketWindow implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0){
            System.out.println(Thread.currentThread().getName() + "已售票" + --ticket);
        }
    }
}

出现了线程安全问题

引入Lock

package com.atguigu.juc;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        SellTicketWindow sellTicketWindow = new SellTicketWindow();

        new Thread(sellTicketWindow, "1号窗口").start();
        new Thread(sellTicketWindow, "2号窗口").start();
        new Thread(sellTicketWindow, "3号窗口").start();
    }
}

class SellTicketWindow implements Runnable{
    private int ticket = 100;

    // 手动上锁、释放锁,首先需要有一把锁
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();//上锁

        try {
            while (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "已售票" + --ticket);
            }
        }finally {
            // 为保证释放锁操作一定执行,需要放到finally中
            lock.unlock();
        }
    }
}

通过Lock完成等待唤醒机制

生产者消费者案例

package com.atguigu.juc;

/**
 * 售货员从啤酒厂上货,张三买货
 */
public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(productor, "啤酒厂").start();
        new Thread(consumer, "张三").start();
    }

}

/**
 * 售货员
 */
class Clerk{
    private int product = 0;

    public synchronized void get(){
        if (product >= 10){
            System.out.println("产品已满!");
        }else {
            System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
        }
    }

    public synchronized void sale(){
        if (product <= 0){
            System.out.println("缺货!");
        }else {
            System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
        }
    }
}

/**
 * 啤酒厂
 */
class Productor implements Runnable{

    private Clerk clerk;

    Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.clerk.get();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable{

    private Clerk clerk;

    Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.clerk.sale();
        }
    }
}

wait和notifyAll实现的等待唤醒机制

package com.atguigu.juc;

/**
 * 售货员从啤酒厂上货,张三买货
 */
public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(productor, "啤酒厂").start();
        new Thread(consumer, "张三").start();
    }

}

/**
 * 售货员
 */
class Clerk{
    private int product = 0;

    public synchronized void get(){
        if (product >= 10){
            System.out.println("产品已满!");
            try {
                // 告知啤酒厂库存满了
                this.wait();
            } catch (InterruptedException e) {
            }
        }else {
            System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
            // 通知张三新货到了
            this.notifyAll();
        }
    }

    public synchronized void sale(){
        if (product <= 0){
            System.out.println("缺货!");
            try {
                // 告知张三没货了
                this.wait();
            } catch (InterruptedException e) {
            }
        }else {
            System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
            // 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
            this.notifyAll();
        }
    }
}

/**
 * 啤酒厂
 */
class Productor implements Runnable{

    private Clerk clerk;

    Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.clerk.get();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable{

    private Clerk clerk;

    Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.clerk.sale();
        }
    }
}

但是这个案例还是存在问题的,有可能会产生死锁

减少库存上限和生产者生产前增加延时

package com.atguigu.juc;

/**
 * 售货员从啤酒厂上货,张三买货
 */
public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(productor, "啤酒厂").start();
        new Thread(consumer, "张三").start();
    }

}

/**
 * 售货员
 */
class Clerk{
    private int product = 0;

    public synchronized void get(){
        if (product >= 1){
            System.out.println("产品已满!");
            try {
                // 告知啤酒厂库存满了
                this.wait();
            } catch (InterruptedException e) {
            }
        }else {
            System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
            // 通知张三新货到了
            this.notifyAll();
        }
    }

    public synchronized void sale(){
        if (product <= 0){
            System.out.println("缺货!");
            try {
                // 告知张三没货了
                this.wait();
            } catch (InterruptedException e) {
            }
        }else {
            System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
            // 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
            this.notifyAll();
        }
    }
}

/**
 * 啤酒厂
 */
class Productor implements Runnable{

    private Clerk clerk;

    Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            this.clerk.get();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable{

    private Clerk clerk;

    Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            this.clerk.sale();
        }
    }
}

程序一直在运行中,结束不了

分析过程如下

解决上述问题的方法是去掉这里的else分支

调整后分析如下

上述问题解决

目前看这样是没问题的,但当生产者和消费者线程增加时,又会出现新的问题

其他代码不变,仅增加一个生产者线程和消费者线程

package com.atguigu.juc;

/**
 * 售货员从啤酒厂上货,张三买货
 */
public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(productor, "啤酒厂").start();
        new Thread(consumer, "张三").start();
        new Thread(productor, "烟厂").start();
        new Thread(consumer, "李四").start();
    }

}

/**
 * 售货员
 */
class Clerk{
    private int product = 0;

    public synchronized void get(){
        if (product >= 1){
            System.out.println("产品已满!");
            try {
                // 告知啤酒厂库存满了
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
        // 通知张三新货到了
        this.notifyAll();
    }

    public synchronized void sale(){
        if (product <= 0){
            System.out.println("缺货!");
            try {
                // 告知张三没货了
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
        // 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
        this.notifyAll();

    }
}

/**
 * 啤酒厂
 */
class Productor implements Runnable{

    private Clerk clerk;

    Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            this.clerk.get();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable{

    private Clerk clerk;

    Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            this.clerk.sale();
        }
    }
}

出现了线程安全问题

这是因为出现了虚假唤醒的情况,分析如下

上述问题在JDK 1.7文档中给出了解决方法

使用while替换if,保证线程被唤醒后先去进行一次判断

完整代码如下

package com.atguigu.juc;

/**
 * 售货员从啤酒厂上货,张三买货
 */
public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor productor = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(productor, "啤酒厂").start();
        new Thread(consumer, "张三").start();
        new Thread(productor, "烟厂").start();
        new Thread(consumer, "李四").start();
    }

}

/**
 * 售货员
 */
class Clerk{
    private int product = 0;

    public synchronized void get(){
        while (product >= 1){
            System.out.println("产品已满!");
            try {
                // 告知啤酒厂库存满了
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
        // 通知张三新货到了
        this.notifyAll();
    }

    public synchronized void sale(){
        while (product <= 0){// 为了避免虚假环境问题,wait()应该总是被使用在while循环中
            System.out.println("缺货!");
            try {
                // 告知张三没货了
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
        // 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
        this.notifyAll();

    }
}

/**
 * 啤酒厂
 */
class Productor implements Runnable{

    private Clerk clerk;

    Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            this.clerk.get();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable{

    private Clerk clerk;

    Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            this.clerk.sale();
        }
    }
}

以上就是完整的使用synchronized结合Object的wait和notify完成的等待唤醒机制

如何使用Lock同步锁完成等待唤醒机制

posted @   刘二水  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示