Lock锁、Condition实现精准通知对象

在了解Lock锁之前我们需要去了解一下Synchronized。

锁是什么?
锁是用于通过多个线程控制对共享资源的访问的工具。

Synchronized是Java中的一个关键字,在并发编程中要考虑线程安全问题,关键字Synchronized保证了同一时刻只有一个线程可以执行代码块或者方法。

Synchronized作用于实例方法
当两个线程同时对一个对象的一个方法进行操作时,只有一个线程能够抢到锁,一个线程抢到该对象的锁之后其他对象就无法去抢到该锁,只有等到获取到锁的对象释放锁之后,其他线程才能去获取。

Synchronized作用域于静态方法
两个线程实例化两个不同的对象,但是访问的方法是静态的,两个线程发生了互斥(即一个线程访问,另一个线程只能等着),因为静态方法是依附于类而不是对象的,当synchronized修饰静态方法时,锁是class对象。

Synchronized作用于同步代码块
为什么要同步代码块?我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。

那么Synchronized和Lock的区别在哪?这时候就要去看看Lock究竟是怎么样的。可以去看一下官方文档
在这里插入图片描述我们来看一下Lock的实现类ReentrantLock,看看源码
在这里插入图片描述
默认的情况下Lock锁使用的是非公平锁,见源码:
在这里插入图片描述

官网给了我们一个Lock锁的书写规范

 class X { 
 private final ReentrantLock lock = new ReentrantLock(); 
  ... public void m() {
   lock.lock();
   block until condition holds try { 
  ... method body } 
  finally { lock.unlock() } 
  } 
  } 

在这里插入图片描述


Synchronized和Lock锁的区别?
1.Synchronized是Java的关键字,Lock锁是java的一个接口有自己的实现类。
2.Synchronized无法判断锁的状态,Lock可以去判断是否换取到了锁。
3.Synchronized是自动释放锁,而Lock需要自己手动去释放锁。
4.Synchronized适合少量的代码同步问题,而Lock锁适合大量的代码的同步问题。
5.Synchronized 可重入锁,不可中断,是非公平锁。Lock也是可重入锁,可以去判断锁的存在,公平锁还是非公平锁是可以去设置的。

为了我们能清晰的看出Lock锁和Synchronized的区别,进行下代码的比较

Synchronized锁:

package com.qingfeng;

public class B {

    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"B").start();
    }
}


class Data2 {

    private int num = 0;

    public synchronized void increment() throws InterruptedException {
            //业务代码
            while (num != 0){
                //等待
                this.wait();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+":"+num);
            //唤醒其他线程
            this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
            //业务代码
            while (num == 0){
                //等待
               this.wait();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+":"+num);
            //通知其他线程
            this.notifyAll();
    }

}



我们来根据官方文档书写一下关于Lock锁的代码示例:

package com.qingfeng;

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

public class A {

    public static void main(String[] args) {
        Data1 data1 = new Data1();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data1.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data1.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"B").start();
    }
}

class Data1 {

    int num=0;

     Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        try {
            lock.lock();
            //业务代码
            while (num != 0){
                //等待
                 condition.await();
            }
            num = 1;
            System.out.println(Thread.currentThread().getName()+":"+num);
            //唤醒其他线程
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {

        try {
            lock.lock();
            //业务代码
            while (num == 0){
                //等待
                condition.await();
            }
            num = 0;
            System.out.println(Thread.currentThread().getName()+":"+num);
            //通知其他线程
            condition.signalAll();

        }finally {
            lock.unlock();
        }
    }

}

通过代码可以看出Synchronized的锁使用起来比较方便,它是自动去释放锁的,而Lock锁需要去取得锁,最后释放锁,Lock锁通过Condition接口来线程等待await() 和唤醒其他线程signal() ,signalAll() 。

那么Condition是什么呢?

condition是一个接口,Condition支持关联多个对象.Condition因素出Object监视器方法( wait , notify和notifyAll )成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock个实现。 Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。

Condition中的方法。
在这里插入图片描述


根据以上内容我们可以通过Condition实现精准通知唤醒,可以按自己规定的顺序去执行。

在进行代码之前我们去了解“虚假唤醒”,就是为什么规定结构要使用while语句。
在这里插入图片描述

下面我们进行代码验证Condition的精准通知唤醒。
按照官方文档的参考:
在这里插入图片描述

代码实现ABC依次进行A–>B–>C

package com.qingfeng;

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

public class C {

    public static void main(String[] args) {
        Data3 data = new Data3();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        },"C").start();

    }
}


class Data3 {

    int num=1;

    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();


    public void printA() throws InterruptedException {

        try {
            //获取锁
            lock.lock();
            while (num != 1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+": AAAAAAAAA");
            num++;
            //唤醒B
            condition2.signal();

        }finally {
            //释放锁
            lock.unlock();

        }



    }

    public void printB() throws InterruptedException {
        try {
            lock.lock();
            while (num != 2 ){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+": BBBBBBBB");
            num++;
            //唤醒通知C
            condition3.signal();
        }finally {
            lock.unlock();

        }

    }

    public void printC() throws InterruptedException {
        try {
            lock.lock();
            while (num != 3 ){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+": CCCCCCC");
            num = 1;
            //唤醒通知A
            condition1.signal();
        }finally {
            lock.unlock();

        }

    }

}



Condition的相关技术要参考官方文档来精准学习,还是比较重要的,比较契合实际开发的技术需求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值