在了解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的相关技术要参考官方文档来精准学习,还是比较重要的,比较契合实际开发的技术需求。