共计 3129 个字符,预计需要花费 8 分钟才能阅读完成。
1.Synchronized 与 Lock 的区别
2.Condition 的根底概念
3. 应用 Condition 循环程序打印 ABC
1.Condition 的根底概念
咱们可能对于 Condition 类都比拟生疏,所以咱们从咱们比拟相熟的 Synchronized 开始比照着学习。
咱们都晓得 Synchronized 都有下图这三个应用办法:
首先是咱们已知的最纯熟的 synchronized 关键字,他是保障线程同步用的,而后是 Thread.notify()(唤醒所有正在期待中的线程),Thread.wait()(将该线程退出期待队列)。
然而咱们的 Lock 接口也有相似的三个办法:
其中,lock()办法对应着 synchronize 的 sync,Lock 接口下的 condition(咱们将在下文进行介绍)中有等同于 notify()的 signal(),和 wait()的 await()。
所以总结地说,Synchronized 和 Lock 有以下区别。
1. 原始形成:
synchronized 是 关键字,属于 JVM 层面
lock 是 具体类,是 api 层面
2. 应用办法
synchronized 不须要用户区手动开释锁,除非运行结束或者抛异样
lock 须要用户手动去开释锁,就有可能呈现死锁景象,个别配合 try finally 来开释锁。
3. 加锁是否偏心
synchronized:非偏心
lock:非偏心(构造方法可抉择,默认非偏心)
4. 期待是否可中断
synchronized 不可中断,除非执行结束或者抛出异样
reentrantlock 可中断,可设置超时办法,设置 interrupt 办法
5,绑定 condition
synchronized 不能绑定
condition 用来实现分组所须要的唤醒的线程们,能够准确唤醒,而不是像 synchronized 随机唤醒一个,或者唤醒全副的线程。
2.Condition 的根底概念
从上文刚刚简略的介绍能够看出,condition 是用来准确唤醒某一个线程的,接下来咱们就来系统地介绍一下 condition:
Cindition,它是用来代替传统的 Object 的 wait()、notify()实现线程间的合作,相比应用 Object 的 wait()、notify(),应用 Condition 的 await()、signal()这种形式实现线程间合作更加平安和高效。因而通常来说比拟举荐应用 Condition,阻塞队列实际上是应用了 Condition 来模仿线程间合作。
3. 应用 Condition 循环程序打印 ABC
既然 Condition 是用来准确唤醒线程的,那么咱们接下来实现一个小 Demo,创立三个线程,让他们按程序循环打印 ABC,A 打印 5 次,B 打印 10 次,C 打印 15 次,并且应用 Condition 准确唤醒:
咱们将要用到一下变量:
// 一个信号量 1 代表打印 A 的线程应该被唤醒,2 代表 B,3 代表 C
private int number = 1;
// 一把锁
private Lock lock = new ReentrantLock();
//c1,c2,c3 用来准确唤醒的线程
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
接下来咱们定义三个办法:
public void print5() {}
public void print10() {}
public void print15() {}
咱们将会启动多个线程,别离运行 print5(),print10(),print15()办法,
通过 condition 管制,在 print5()完结后,启动 print10(),print10()完结后,启动 print15()办法,始终循环。
接下别离来看一下三个函数的函数体:
public void print5() {
// 加锁,每次只有一个线程能运行
lock.lock();
try {//number 作为信号量,为 1 的时候就应该执行 print5()办法
while (number != 1) {
// 如果不是 1,此线程就期待
c1.await();}
// 执行打印办法
for (int i = 1; i <= 5; i++) {System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 接下来应该轮到 print10()办法启动了
number = 2;
// 唤醒 print10()所领有的的线程
c2.signal();} catch (InterruptedException e) {e.printStackTrace();
} finally {lock.unlock();
}
}
public void print10() {
// 加锁,每次只有一个线程能运行
lock.lock();
try {//number 作为信号量,为 2 的时候就应该执行 print10()办法
while (number != 2) {
// 如果不是 2,此线程就期待
c2.await();}
for (int i = 1; i <= 10; i++) {System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 接下来应该轮到 print15()办法启动了
number = 3;
// 唤醒 print15()所领有的的线程
c3.signal();} catch (InterruptedException e) {e.printStackTrace();
} finally {lock.unlock();
}
}
public void print15() {
// 加锁,每次只有一个线程能运行
lock.lock();
try {while (number != 3) {
// 如果不是 3,此线程就期待
c3.await();}
for (int i = 1; i <= 15; i++) {System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 接下来又回到 print5()办法启动了
number = 1;
// 唤醒 print5()所在线程
c1.signal();} catch (InterruptedException e) {e.printStackTrace();
} finally {lock.unlock();
}
}
从正文咱们能够看出,通过 Lock 加锁,能够每次只让一个办法进行,通过 number 信号量和三个 condition,能够准确地唤醒对应地线程,接下来,咱们别离创立线程,来执行一下这三个办法:
public static void main(String[] args) {ShareResources shareResources = new ShareResources();
for (int i = 1; i < 5; i++) {new Thread(() -> {shareResources.print5();
}, "A").start();}
for (int i = 1; i < 5; i++) {new Thread(() -> {shareResources.print10();
}, "B").start();}
for (int i = 1; i < 5; i++) {new Thread(() -> {shareResources.print15();
}, "C").start();}
}
再看一下执行后果:
执行胜利!
总结:
通过这篇博客,咱们总结了 Synchronized 与 Lock 的区别,得出 synchronized 是能够全副换成 lock 的,而且 lock 的管制比 synchronized 更家准确和自在一点,还是用了 Lock 进行线程的程序循环输入。