當(dāng)前位置:首頁>職場>如何理解單線程和多線程(多線程間的5種通信方式都說不出來)
發(fā)布時(shí)間:2024-01-24閱讀(16)
有兩個(gè)線程,A 線程向一個(gè)集合里面依次添加元素“abc”字符串,一共添加十次,當(dāng)添加到第五次的時(shí)候,希望 B 線程能夠收到 A 線程的通知,然后 B 線程執(zhí)行相關(guān)的業(yè)務(wù)操作線程間通信的模型有兩種:共享內(nèi)存和消息傳遞,以下方式都是基本這兩種模型來實(shí)現(xiàn)的,下面我們就來說一說關(guān)于如何理解單線程和多線程?我們一起去了解并探討一下這個(gè)問題吧!

如何理解單線程和多線程
問題有兩個(gè)線程,A 線程向一個(gè)集合里面依次添加元素“abc”字符串,一共添加十次,當(dāng)添加到第五次的時(shí)候,希望 B 線程能夠收到 A 線程的通知,然后 B 線程執(zhí)行相關(guān)的業(yè)務(wù)操作。線程間通信的模型有兩種:共享內(nèi)存和消息傳遞,以下方式都是基本這兩種模型來實(shí)現(xiàn)的。
一、使用 volatile 關(guān)鍵字基于 volatile 關(guān)鍵字來實(shí)現(xiàn)線程間相互通信是使用共享內(nèi)存的思想。大致意思就是多個(gè)線程同時(shí)監(jiān)聽一個(gè)變量,當(dāng)這個(gè)變量發(fā)生變化的時(shí)候 ,線程能夠感知并執(zhí)行相應(yīng)的業(yè)務(wù)。這也是最簡單的一種實(shí)現(xiàn)方式
public class TestSync { //定義共享變量來實(shí)現(xiàn)通信,它需要volatile修飾,否則線程不能及時(shí)感知 static volatile boolean notice = false; public static void main(String[] args) { List<String> list = new ArrayList<>(); //線程A Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i ) { list.add("abc"); System.out.println("線程A添加元素,此時(shí)list的size為:" list.size()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) notice = true; } }); //線程B Thread threadB = new Thread(() -> { while (true) { if (notice) { System.out.println("線程B收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); break; } } }); //需要先啟動(dòng)線程B threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 再啟動(dòng)線程A threadA.start(); }}
Object 類提供了線程間通信的方法:wait()、notify()、notifyAll(),它們是多線程通信的基礎(chǔ),而這種實(shí)現(xiàn)方式的思想自然是線程間通信。
注意:wait/notify 必須配合 synchronized 使用,wait 方法釋放鎖,notify 方法不是放鎖。wait 是指在一個(gè)已經(jīng)進(jìn)入了同步鎖的線程內(nèi),讓自己暫時(shí)讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運(yùn)行,只有其他線程調(diào)用了notify(),notify并不釋放鎖,只是告訴調(diào)用過wait()的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因?yàn)殒i還在別人手里,別人還沒釋放,調(diào)用 wait() 一個(gè)或多個(gè)線程就會(huì)解除 wait 狀態(tài),重新參與競爭對象鎖,程序如果可以再次得到鎖,就可以繼續(xù)向下運(yùn)行。
public class TestSync { public static void main(String[] args) { //定義一個(gè)鎖對象 Object lock = new Object(); List<String> list = new ArrayList<>(); // 線程A Thread threadA = new Thread(() -> { synchronized (lock) { for (int i = 1; i <= 10; i ) { list.add("abc"); System.out.println("線程A添加元素,此時(shí)list的size為:" list.size()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) lock.notify();//喚醒B線程 } } }); //線程B Thread threadB = new Thread(() -> { while (true) { synchronized (lock) { if (list.size() != 5) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程B收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); } } }); //需要先啟動(dòng)線程B threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //再啟動(dòng)線程A threadA.start(); }}
由輸出結(jié)果,在線程 A 發(fā)出 notify() 喚醒通知之后,依然是走完了自己線程的業(yè)務(wù)之后,線程 B 才開始執(zhí)行,正好說明什么 notify() 不釋放鎖,而 wait() 釋放鎖。
三、使用JUC工具類 CountDownLatchjdk1.5 之后在java.util.concurrent包下提供了很多并發(fā)編程相關(guān)的工具類,簡化了并發(fā)編程代碼的書寫,CountDownLatch 基于 AQS 框架,相當(dāng)于也是維護(hù)了一個(gè)線程間共享變量 state。
public class TestSync { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(1); List<String> list = new ArrayList<>(); //線程A Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i ) { list.add("abc"); System.out.println("線程A添加元素,此時(shí)list的size為:" list.size()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) countDownLatch.countDown(); } }); //線程B Thread threadB = new Thread(() -> { while (true) { if (list.size() != 5) { try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程B收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); break; } }); //需要先啟動(dòng)線程B threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //再啟動(dòng)線程A threadA.start(); }}
public class TestSync { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); List<String> list = new ArrayList<>(); //線程A Thread threadA = new Thread(() -> { lock.lock(); for (int i = 1; i <= 10; i ) { list.add("abc"); System.out.println("線程A添加元素,此時(shí)list的size為:" list.size()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) condition.signal(); } lock.unlock(); }); //線程B Thread threadB = new Thread(() -> { lock.lock(); if (list.size() != 5) { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程B收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); lock.unlock(); }); threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadA.start(); }}
這種方式使用起來并不是很好,代碼編寫復(fù)雜,而且線程 B 在被 A 喚醒之后由于沒有獲取鎖還是不能立即執(zhí)行,也就是說,A 在喚醒操作之后,并不釋放鎖。這種方法跟 Object 的 wait()/notify() 一樣。
五、基本 LockSupport 實(shí)現(xiàn)線程間的阻塞和喚醒LockSupport 是一種非常靈活的實(shí)現(xiàn)線程間阻塞和喚醒的工具,使用它不用關(guān)注是等待線程先進(jìn)行還是喚醒線程先運(yùn)行,但是得知道線程的名字。
public class TestSync { public static void main(String[] args) { List<String> list = new ArrayList<>(); //線程B final Thread threadB = new Thread(() -> { if (list.size() != 5) { LockSupport.park(); } System.out.println("線程B收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); }); //線程A Thread threadA = new Thread(() -> { for (int i = 1; i <= 10; i ) { list.add("abc"); System.out.println("線程A添加元素,此時(shí)list的size為:" list.size()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) LockSupport.unpark(threadB); } }); threadA.start(); threadB.start(); }}
原文鏈接:https://mp.weixin.qq.com/s/zRXvaR_2j0FSHPMqKACW6A
歡迎分享轉(zhuǎn)載→http://www.avcorse.com/read-224769.html
Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖