Java五種方式實現(xiàn)多線程循環(huán)打印問題
三個線程T1、T2、T3輪流打印ABC,打印n次,如ABCABCABCABC…
N個線程循環(huán)打印1-100…
wait-notify
循環(huán)打印問題可以通過設置目標值,每個線程想打印目標值,如果拿到鎖后這次輪到的數(shù)不是它想要的就進入wait
class Wait_Notify_ABC {
private int num;
private static final Object Lock = new Object();
private void print_ABC(int target) {
synchronized (Lock) {
//循環(huán)打印
for (int i = 0; i < 10; i++) {
while (num % 3 != target) {
try {
Lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.print(Thread.currentThread().getName());
Lock.notifyAll();
}
}
}
public static void main(String[] args) {
Wait_Notify_ABC wait_notify_abc = new Wait_Notify_ABC();
new Thread(() -> {
wait_notify_abc.print_ABC(0);
}, "A").start();
new Thread(() -> {
wait_notify_abc.print_ABC(1);
}, "B").start();
new Thread(() -> {
wait_notify_abc.print_ABC(2);
}, "C").start();
}
}
打印1-100問題可以理解為有個全局計數(shù)器記錄當前打印到了哪個數(shù),其它就和循環(huán)打印ABC問題相同。
class Wait_Notify_100 {
private int num;
private static final Object LOCK = new Object();
private int maxnum = 100;
private void printABC(int targetNum) {
while (true) {
synchronized (LOCK) {
while (num % 3 != targetNum) {
if (num >= maxnum) {
break;
}
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (num >= maxnum) {
break;
}
num++;
System.out.println(Thread.currentThread().getName() + ": " + num);
LOCK.notifyAll();
}
}
}
public static void main(String[] args) {
Wait_Notify_100 wait_notify_100 = new Wait_Notify_100();
new Thread(() -> {
wait_notify_100.printABC(0);
}, "thread1").start();
new Thread(() -> {
wait_notify_100.printABC(1);
}, "thread2").start();
new Thread(() -> {
wait_notify_100.printABC(2);
}, "thread3").start();
}
}
join方式
一個線程內(nèi)調(diào)用另一個線程的join()方法可以讓另一個線程插隊執(zhí)行,比如Main方法里調(diào)用了A.join(),那么此時cpu會去執(zhí)行A線程中的任務,執(zhí)行完后再看Main是否能搶到運行權。所以對于ABC,我們可以對B說讓A插隊,對C說讓B插隊
class Join_ABC {
static class printABC implements Runnable {
private Thread beforeThread;
public printABC(Thread beforeThread) {
this.beforeThread = beforeThread;
}
@Override
public void run() {
if (beforeThread != null) {
try {
beforeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread t1 = new Thread(new printABC(null), "A");
Thread t2 = new Thread(new printABC(t1), "B");
Thread t3 = new Thread(new printABC(t2), "C");
t1.start();
t2.start();
t3.start();
Thread.sleep(100);
}
}
}
ReentrantLock
同理,synchronized和reentrantlock都是我們常用的加鎖方式,不過后者可以中斷,可以實現(xiàn)公平鎖,可以使用condition…但是需要我們手動釋放鎖。jdk8后二者性能差不多,畢竟synchronized有鎖升級的過程嘛。
class ReentrantLock_ABC {
private int num;
private Lock lock = new ReentrantLock();
private void printABC(int targetNum) {
for (int i = 0; i < 100; ) {
lock.lock();
if (num % 3 == targetNum) {
num++;
i++;
System.out.print(Thread.currentThread().getName());
}
lock.unlock();
}
}
public static void main(String[] args) {
Lock_ABC lockABC = new Lock_ABC();
new Thread(() -> {
lockABC.printABC(0);
}, "A").start();
new Thread(() -> {
lockABC.printABC(1);
}, "B").start();
new Thread(() -> {
lockABC.printABC(2);
}, "C").start();
}
}
ReentrantLock+Condition
以上方式如果線程搶到鎖后發(fā)現(xiàn)自己無法執(zhí)行任務,那么就釋放,然后別的線程再搶占再看是不是自己的…這種方式比較耗時,如果我們能實現(xiàn)精準喚醒鎖呢,即A完成任務后喚醒它的下一個即B,這就用到我們的Condition啦
class ReentrantLock_Condition_ABC {
private int num;
private static Lock lock = new ReentrantLock();
private static Condition c1 = lock.newCondition();
private static Condition c2 = lock.newCondition();
private static Condition c3 = lock.newCondition();
private void printABC(int targetNum, Condition currentThread, Condition nextThread) {
for (int i = 0; i < 100; ) {
lock.lock();
try {
while (num % 3 != targetNum) {
currentThread.await(); //阻塞當前線程
}
num++;
i++;
System.out.print(Thread.currentThread().getName());
nextThread.signal(); //喚醒下一個線程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLock_Condition_ABC reentrantLockConditionAbc = new ReentrantLock_Condition_ABC();
new Thread(() -> {
reentrantLockConditionAbc.printABC(0, c1, c2);
}, "A").start();
new Thread(() -> {
reentrantLockConditionAbc.printABC(1, c2, c3);
}, "B").start();
new Thread(() -> {
reentrantLockConditionAbc.printABC(2, c3, c1);
}, "C").start();
}
}
Semaphore
小伙伴們有沒有想到過,在生產(chǎn)者消費者模型中我們有哪幾種實現(xiàn)方式呢?wait\notify,ReentrantLock,Semaphone,阻塞隊列,管道輸入輸出流。
對的就是Semaphone。
Semaphore有acquire方法和release方法。 當調(diào)用acquire方法時線程就會被阻塞,直到獲得許可證為止。 當調(diào)用release方法時將向Semaphore中添加一個許可證。如果沒有獲取許可證的線程, Semaphore只是記錄許可證的可用數(shù)量。
使用Semaphore也可以實現(xiàn)精準喚醒。
class SemaphoreABC {
private static Semaphore s1 = new Semaphore(1); //因為先執(zhí)行線程A,所以這里設s1的計數(shù)器為1
private static Semaphore s2 = new Semaphore(0);
private static Semaphore s3 = new Semaphore(0);
private void printABC(Semaphore currentThread, Semaphore nextThread) {
for (int i = 0; i < 10; i++) {
try {
currentThread.acquire(); //阻塞當前線程,即信號量的計數(shù)器減1為0
System.out.print(Thread.currentThread().getName());
nextThread.release(); //喚醒下一個線程,即信號量的計數(shù)器加1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
SemaphoreABC printer = new SemaphoreABC();
new Thread(() -> {
printer.printABC(s1, s2);
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
printer.printABC(s2, s3);
}, "B").start();
Thread.sleep(100);
new Thread(() -> {
printer.printABC(s3, s1);
}, "C").start();
}
}
到此這篇關于Java五種方式實現(xiàn)多線程循環(huán)打印問題的文章就介紹到這了,更多相關Java 多線程循環(huán)打印內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)冒泡排序算法及對其的簡單優(yōu)化示例
這篇文章主要介紹了Java實現(xiàn)冒泡排序算法及對其的簡單優(yōu)化示例,冒泡排序的最差時間復雜度為O(n^2),最優(yōu)時間復雜度為O(n),存在優(yōu)化的余地,需要的朋友可以參考下2016-05-05
Springboot使用redis實現(xiàn)接口Api限流的示例代碼
本文主要介紹了Springboot使用redis實現(xiàn)接口Api限流的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07
Spring Boot整合ElasticSearch實現(xiàn)多版本兼容的方法詳解
簡單說,ElasticSearch(簡稱 ES)是搜索引擎,是結構化數(shù)據(jù)的分布式搜索引擎。下面這篇文章主要給大家介紹了關于Spring Boot整合ElasticSearch實現(xiàn)多版本兼容的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧2018-05-05

