首页 > 其他分享 >JUC系列之(七)Lock同步锁

JUC系列之(七)Lock同步锁

时间:2024-02-29 19:36:48浏览次数:28  
标签:JUC product 同步 Thread Lock void start new public

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同步锁完成等待唤醒机制

标签:JUC,product,同步,Thread,Lock,void,start,new,public
From: https://www.cnblogs.com/wzzzj/p/18045240

相关文章

  • JUC系列之(六)实现Callable接口
    实现Callable接口创建执行线程的第三种方式,共四种比较Runnable和Callable:Callable方法可以有返回值,并且可以抛出异常。由于Callable的方法有返回值,需要FutureTask的支持,用于接收运算结果。FutureTask是Future接口的实现类。使用packagecom.atguigu.juc;importjava.......
  • JUC系列之(五)CountDownLatch闭锁
    CountDownLatch闭锁闭锁:延迟当前线程的进度,直到其他线程都执行完成当前线程才继续执行。示例:计算多线程操作耗费时间以下操作时无法正常计算多线程操作耗时的packagecom.atguigu.juc;publicclassTestCountDownLatch{publicstaticvoidmain(String[]args){......
  • JUC系列之(四)ConcurrentHashMap锁分段机制
    ConcurrentHashMap锁分段机制1.关于HashMap和HashTableHashMap:线程不安全HashTable:效率低:操作时锁整个表复合操作会带来安全问题//table.contains()和table.put()分别都是加了锁的,但是像下述复合操作,一个线程判断完之后CPU可能被其他线程抢夺,带来安全问题if(!table.c......
  • JUC系列之(三)原子变量
    原子变量-CAS算法1.i++的原子性问题i++的计算原理:读-改-写inttemp=i;i=i+1;将i++赋给其他变量的时候会将temp的值赋给其他变量,比如:inti=10;i=i++;//这里i的值就是10,i++的值实际上就是临时变量temp的值i++的原子性问题实例packagecom.atguigu.juc;......
  • JUC系列之(二)volatile关键字
    volatile关键字-内存可见性引出内存可见性问题的示例:packagecom.atguigu.juc;publicclassTestVolatile{publicstaticvoidmain(String[]args){//线程threadDemo修改共享变量的值ThreadDemothreadDemo=newThreadDemo();newThrea......
  • 经典同步问题及其伪代码实现
    1、生产者消费者问题信号量版本://定义缓冲区大小bufferSize=10//定义互斥锁和信号量mutex=Semaphore(1)full=Semaphore(0)empty=Semaphore(bufferSize)//定义生产者函数defproducer():whiletrue://生成数据data=generateData()......
  • Linux_时间同步和开机自启动
    时间同步时间同步可分为几部分的内容:统一时钟源,硬件同步,软件同步。时间同步--时空一体--(时间-空间-物体)1.timespaceobject速度加速度角速度位置距离角度2.GPS卫星GPS基站GPS终端空间--太阳大气层地面空间干扰-环境遮蔽--空间隔......
  • 同步和异步
    概述在数字电路中经常有同步\异步的概念,异步输入指的是输入信号和时钟无关,同步指的是输入信号和时钟信号有关实际开发中,经常有同步清0,经常有同步清0,异步清0,同步复位,异步复位等概念异步电路:异步电路主要是组合逻辑电路,其逻辑输出与任何时钟信号都没有关系//时钟信......
  • CF1209G2 Into Blocks (hard version) 题解
    Description给你\(n\),\(q\),\(n\)表示序列长度,\(q\)表示操作次数。我们需要达成这么一个目标状态:如果存在\(x\)这个元素,那么必须满足所有\(x\)元素都必须在序列中连续。然后你可以进行这么一种操作,将所有的\(x\)元素的变为任意你指定的\(y\)元素,并且花费\(cnt[x......
  • 北斗同步时钟服务器(NTP网络时间服务器)应用于计算机网络系统
    北斗同步时钟服务器(NTP网络时间服务器)应用于计算机网络系统北斗同步时钟服务器(NTP网络时间服务器)应用于计算机网络系统京准电子科技官微——ahjzsz前言近几年来,随着计算机自动化系统水平的提高,在各大计算机监控系统、微机保护装置、微机故障录波装置以及各类数据管理机得......