Java線程通信詳解
線程通信用來保證線程協(xié)調(diào)運行,一般在做線程同步的時候才需要考慮線程通信的問題。
1、傳統(tǒng)的線程通信
通常利用Objeclt類提供的三個方法:
- wait() 導(dǎo)致當(dāng)前線程等待,并釋放該同步監(jiān)視器的鎖定,直到其它線程調(diào)用該同步監(jiān)視器的notify()或者notifyAll()方法喚醒線程。
- notify(),喚醒在此同步監(jiān)視器上等待的線程,如果有多個會任意選擇一個喚醒
- notifyAll() 喚醒在此同步監(jiān)視器上等待的所有線程,這些線程通過調(diào)度競爭資源后,某個線程獲取此同步監(jiān)視器的鎖,然后得以運行。
這三個方法必須由同步監(jiān)視器對象調(diào)用,分為兩張情況:
同步方法時,由于同步監(jiān)視器為this對象,所以可以直接調(diào)用這三個方法。
示例如下:
public class SyncMethodThreadCommunication {
static class DataWrap{
int data = 0;
boolean flag = false;
public synchronized void addThreadA(){
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = true;
notify();
}
public synchronized void addThreadB() {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = false;
notify();
}
}
static class ThreadA extends Thread {
private DataWrap data;
public ThreadA(DataWrap dataWrap) {
this.data = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
data.addThreadA();
}
}
}
static class ThreadB extends Thread {
private DataWrap data;
public ThreadB(DataWrap dataWrap) {
this.data = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
data.addThreadB();
}
}
}
public static void main(String[] args) {
//實現(xiàn)兩個線程輪流對數(shù)據(jù)進(jìn)行加一操作
DataWrap dataWrap = new DataWrap();
new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
}
}
同步代碼塊時,需要使用監(jiān)視器對象調(diào)用這三個方法。
示例如下:
public class SyncBlockThreadComminication {
static class DataWrap{
boolean flag;
int data;
}
static class ThreadA extends Thread{
DataWrap dataWrap;
public ThreadA(DataWrap dataWrap){
this.dataWrap = dataWrap;
}
@Override
public void run() {
for(int i = 0 ; i < 10; i++) {
synchronized (dataWrap) {
if (dataWrap.flag) {
try {
dataWrap.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
dataWrap.flag = true;
dataWrap.notify();
}
}
}
}
static class ThreadB extends Thread{
DataWrap dataWrap;
public ThreadB(DataWrap dataWrap){
this.dataWrap = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (dataWrap) {
if (!dataWrap.flag) {
try {
dataWrap.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
dataWrap.flag = false;
dataWrap.notify();
}
}
}
}
public static void main(String[] args) {
//實現(xiàn)兩個線程輪流對數(shù)據(jù)進(jìn)行加一操作
DataWrap dataWrap = new DataWrap();
new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
}
}
2、使用Condition控制線程通信
當(dāng)使用Lock對象保證同步時,則使用Condition對象來保證協(xié)調(diào)。
示例如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.sun.media.sound.RIFFInvalidDataException;
import javafx.scene.chart.PieChart.Data;
public class SyncLockThreadCommunication {
static class DataWrap {
int data;
boolean flag;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void addThreadA() {
lock.lock();
try {
if (flag) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = true;
condition.signal();
} finally {
lock.unlock();
}
}
public void addThreadB() {
lock.lock();
try {
if (!flag) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = false;
condition.signal();
} finally {
lock.unlock();
}
}
}
static class ThreadA extends Thread{
DataWrap dataWrap;
public ThreadA(DataWrap dataWrap) {
this.dataWrap = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
dataWrap.addThreadA();
}
}
}
static class ThreadB extends Thread{
DataWrap dataWrap;
public ThreadB(DataWrap dataWrap) {
this.dataWrap = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
dataWrap.addThreadB();
}
}
}
public static void main(String[] args) {
//實現(xiàn)兩個線程輪流對數(shù)據(jù)進(jìn)行加一操作
DataWrap dataWrap = new DataWrap();
new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
}
}
其中Condition對象的await(), singal(),singalAll()分別對應(yīng)wait(),notify()和notifyAll()方法。
3、使用阻塞隊列BlockingQueue控制線程通信
BlockingQueue是Queue接口的子接口,主要用來做線程通信使用,它具有一個特征:當(dāng)生產(chǎn)者線程試圖向BlockingQueue中放入元素時,如果隊列已滿,則該線程被阻塞;當(dāng)消費者線程試圖從BlockingQueue中取出元素時,如果隊列已空,則該線程被阻塞。這兩個特征分別對應(yīng)兩個支持阻塞的方法,put(E e)和take()
示例如下:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueThreadComminication {
static class DataWrap{
int data;
}
static class ThreadA extends Thread{
private BlockingQueue<DataWrap> blockingQueue;
public ThreadA(BlockingQueue<DataWrap> blockingQueue, String name) {
super(name);
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
DataWrap dataWrap = blockingQueue.take();
dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ThreadB extends Thread{
private BlockingQueue<DataWrap> blockingQueue;
private DataWrap dataWrap;
public ThreadB(BlockingQueue<DataWrap> blockingQueue, DataWrap dataWrap, String name) {
super(name);
this.blockingQueue = blockingQueue;
this.dataWrap = dataWrap;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
blockingQueue.put(dataWrap);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
///實現(xiàn)兩個線程輪流對數(shù)據(jù)進(jìn)行加一操作
DataWrap dataWrap = new DataWrap();
BlockingQueue<DataWrap> blockingQueue = new ArrayBlockingQueue<>(1);
new ThreadA(blockingQueue, "Consumer").start();
new ThreadB(blockingQueue, dataWrap, "Producer").start();
}
}
BlockingQueue共有五個實現(xiàn)類:
ArrayBlockingQueue 基于數(shù)組實現(xiàn)的BlockingQueue隊列
LinkedBlockingQueue 基于鏈表實現(xiàn)的BlockingQueue隊列
PriorityBlockingQueue 中元素需實現(xiàn)Comparable接口,其中元素的排序是按照Comparator進(jìn)行的定制排序。
SynchronousQueue 同步隊列,要求對該隊列的存取操作必須是交替進(jìn)行。
DelayQueue 集合元素必須實現(xiàn)Delay接口,隊列中元素排序按照Delay接口方法getDelay()的返回值進(jìn)行排序。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis-plus和Mybatis出現(xiàn)版本不兼容的問題解決
MyBatis-Plus?與?MyBatis?之間的兼容性問題通常是由于版本不匹配引起的,本文主要介紹了Mybatis-plus和Mybatis出現(xiàn)版本不兼容的問題解決,具有一定的參考價值,感興趣的可以了解一下2024-08-08
Spring 事務(wù)事件監(jiān)控及實現(xiàn)原理解析
本文首先會使用實例進(jìn)行講解Spring事務(wù)事件是如何使用的,然后會講解這種使用方式的實現(xiàn)原理。感興趣的朋友跟隨小編一起看看吧2018-09-09
Java如何通過反射機(jī)制獲取數(shù)據(jù)類對象的屬性及方法
文章介紹了如何使用Java反射機(jī)制獲取類對象的所有屬性及其對應(yīng)的get、set方法,以及如何通過反射機(jī)制實現(xiàn)類對象的實例化,感興趣的朋友跟隨小編一起看看吧2025-01-01
Java實現(xiàn)SMS短信通發(fā)送手機(jī)驗證碼案例講解
這篇文章主要介紹了Java實現(xiàn)SMS短信通發(fā)送手機(jī)驗證碼案例講解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Spring Boot集成MinIO對象存儲服務(wù)器操作步驟
通過Spring Boot集成MinIO,你可以在應(yīng)用中方便地進(jìn)行文件的存儲和管理,本文給大家分享Spring Boot集成MinIO對象存儲服務(wù)器詳細(xì)操作步驟,感興趣的朋友一起看看吧2024-01-01
SpringBoot集成內(nèi)存數(shù)據(jù)庫Derby的實踐
像H2、hsqldb、derby、sqlite這樣的內(nèi)存數(shù)據(jù)庫,小巧可愛,做小型服務(wù)端演示程序,非常好用。最大特點就是不需要你另外安裝一個數(shù)據(jù)庫。本文主要介紹了SpringBoot集成內(nèi)存數(shù)據(jù)庫Derby,感興趣的可以了解一下2021-09-09
Mybatis-Plus中Mapper的接口文件與xml文件相關(guān)的坑記錄
這篇文章主要介紹了Mybatis-Plus中Mapper的接口文件與xml文件相關(guān)的坑記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01

