Java線程阻塞和喚醒的幾種方式詳解
Object類自帶的方法
使用wait()方法來阻塞線程,使用notify()和notifyAll()方法來喚醒線程。 調(diào)用wait()方法后,線程將被阻塞,wait()方法將會釋放當前持有的監(jiān)視器鎖(monitor),直到有線程調(diào)用notify/notifyAll()方法后方能繼續(xù)執(zhí)行。 notify/notifyAll()方法只是解除了等待線程的阻塞,并不會馬上釋放監(jiān)視器鎖,而是在相應的被synchronized關鍵字修飾的同步方法或同步代碼塊執(zhí)行結(jié)束后才自動釋放鎖。 默認使用非公平鎖,無法修改。
缺點:
- 使用幾個方法時,必須處于被synchronized關鍵字修飾的同步方法或同步代碼塊中,否則程序運行時,會拋出IllegalMonitorStateException異常。
- 線程的喚醒必須在線程阻塞之后,否則,當前線程被阻塞之后,一直沒有喚醒,線程將會一直等待下去(對比LockSupport)
public class SynchronizedDemo {
// 三個線程交替打印ABC
public static void main(String[] args) {
Print print = new Print();
new Thread(() -> {
while (true) {
print.printA();
}
}, "A").start();
new Thread(() -> {
while (true) {
print.printB();
}
}, "B").start();
new Thread(() -> {
while (true) {
print.printC();
}
}, "C").start();
}
}
class Print {
Object object = new Object();
int num = 1;
public void printA() {
synchronized (object) {
try {
while (num != 1) {
object.wait();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void printB() {
synchronized (object) {
try {
while (num != 2) {
object.wait();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void printC() {
synchronized (object) {
try {
while (num != 3) {
object.wait();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Condition接口
使用await()方法來阻塞線程,signal()/singnalAll()方法來喚醒線程。
需要使用lock對象的newCondition()方法獲得Condition條件對象(可有多個)。
可實現(xiàn)公平鎖,默認是非公平鎖
缺點:
- 必須被Lock包裹,否則會在運行時拋出IllegalMonitorStateException異常。
- 線程的喚醒必須在線程阻塞之后
- Lock的實現(xiàn)是基于AQS,效率稍高于synchronized
public class ConditionDemo {
// 三個線程交替打印ABC
public static void main(String[] args) {
Print print = new Print();
new Thread(() -> {
while (true) {
print.printA();
}
}, "A").start();
new Thread(() -> {
while (true) {
print.printB();
}
}, "B").start();
new Thread(() -> {
while (true) {
print.printC();
}
}, "C").start();
}
}
class Print {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1;
public void printA() {
lock.lock();
try {
while (num != 1) {
condition1.await();
}
for (int i = 0; i < 5; ++i) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (num != 2) {
condition2.await();
}
for (int i = 0; i < 10; ++i) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (num != 3) {
condition3.await();
}
for (int i = 0; i < 15; ++i) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
LockSupport
使用park()來阻塞線程,用unpark()方法來喚醒線程。 這里有一個許可證的概念,許可不能累積,并且最多只能有一個許可,只有1和0的區(qū)別。
特點:
- 使用靈活,可以直接使用
- 線程喚醒可在線程阻塞之前,因為調(diào)用unpark()方法后,線程已經(jīng)獲得了一個許可證(但也只能有一個許可證),之后阻塞時,可以直接使用這個許可證來通行。
- 效率高
public class LockSupportDemo {
// 三個線程交替打印ABC
public static void main(String[] args) throws Exception {
Print print = new Print();
Thread threadA = new Thread(() -> {
while (true) {
print.printA();
}
}, "A");
Thread threadB = new Thread(() -> {
while (true) {
print.printB();
}
}, "B");
Thread threadC = new Thread(() -> {
while (true) {
print.printC();
}
}, "C");
threadA.start();
threadB.start();
threadC.start();
while (true) {
LockSupport.unpark(threadA);
LockSupport.unpark(threadB);
LockSupport.unpark(threadC);
}
}
}
class Print {
private int num = 1;
public void printA() {
while (num != 1) {
LockSupport.park();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
}
public void printB() {
while (num != 2) {
LockSupport.park();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
}
public void printC() {
while (num != 3) {
LockSupport.park();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
}
}
到此這篇關于Java線程阻塞和喚醒的幾種方式詳解的文章就介紹到這了,更多相關Java線程阻塞和喚醒內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java基本教程之java線程等待與java喚醒線程 java多線程教程
這篇文章主要介紹了對線程等待/喚醒方法,文中使用了多個示例,大家參考使用吧2014-01-01
SpringMVC+EasyUI實現(xiàn)頁面左側(cè)導航菜單功能
這篇文章主要介紹了SpringMVC+EasyUI實現(xiàn)頁面左側(cè)導航菜單功能,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09

