1、執行緒同步
-
多個執行緒操作共享資料時,出現的執行緒安全問題
-
解決方式:
-
方式一:同步代碼塊---不能包含多了,也不能包含少了
synchronized(同步監視器){ //需要被同步的代碼 }-
說明:操作共享資料的代碼==需要被同步的代碼
共享資料:多個執行緒共同操作的變數 -
同步監視器:鎖,任何一個類的物件都可充當為鎖
要求:多個執行緒必須要共用同一把鎖
-
對于繼承Thread類創建多執行緒方法
- 同步監視器可以考慮使用定義靜態類物件
- 可以考慮使用當前類物件synchronized (類名.class)
-
對于實作Runnable介面創建多執行緒方法
- 同步監視器可以考慮使用 this
-
-
-
方式二:同步方法: 如果操作共享資料的代碼完整的宣告在一個方法中,將此方法宣告為同步的
private synchronized void show(){ //同步監視器 this } private static synchronized void show(){ //同步監視器:當前類本身 }-
同步方法仍然涉及到同步監視器,只是不需要顯式的宣告
-
非靜態的同步方法,同步監視器是:this
靜態的同步方法,同步監視器是:當前類本身
-
-
方式三:Lock(鎖) jdk5.0新增
-
實體化ReentrantLock
-
呼叫lock()方法
-
呼叫解鎖方法unlock()
package com.han.java.ere; import java.util.concurrent.locks.ReentrantLock; /** * 解決執行緒安全的方式三:lock--- jdk5.0新增 * @author hlrstart * @create 2021-04-04-9:35 */ public class LockTest { public static void main(String[] args) { Windw win=new Windw(); Thread t1=new Thread(win); Thread t2=new Thread(win); Thread t3=new Thread(win); t1.setName("視窗一"); t2.setName("視窗二"); t3.setName("視窗三"); t1.start(); t2.start(); t3.start(); } } class Windw implements Runnable{ private int sticket=100; // 1.實體化ReentrantLock private ReentrantLock lock=new ReentrantLock() ; @Override public void run() { while(true){ try { // 2.呼叫lock()方法 lock.lock(); if (sticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": 票號為" + sticket); sticket--; } else { break; } }finally { // 3.呼叫解鎖方法 lock.unlock(); } } } }
-
-
-
同步的方式優缺點
- 優點:解決了執行緒安全問題
- 局限性:操作同步代碼時,是單執行緒的程序,效率低
-
執行緒死鎖問題
-
不同的執行緒分別占用對方需要的資源不放棄,都在等待對方釋放自己需要的資源,
-
出現死鎖,不會出現例外,不會出現提示,所有執行緒都處于阻塞狀態,無法繼續,
-
2、執行緒通信
涉及到的三個方法
-
wait():一旦執行此方法,當前執行緒進入阻塞狀態,并釋放同步監視器
-
notify():一旦執行此方法,就會喚醒被wait()的一個執行緒,若有多個執行緒被wait(),就喚醒優先級高的那一個
-
notifyAll() :一旦執行此方法,就會喚醒所有被wait()的執行緒
說明
- wait()、notify()、notifyAll() 三個方法必須使用在同步代碼塊或同步方法中,
- wait()、notify()、notifyAll() 三個方法的呼叫者必須是同步代碼塊或同步方法中的同步監視器,否則會出現例外,
- wait()、notify()、notifyAll() 三個方法是定義在java.lang.Object類中,
3、JDK5.0新增執行緒創建方式
新增方式一:實作Callable介面
-
與使用Runnable相比,Callable功能更強大些
- call()方法可以有回傳值
- call()方法可以拋出例外
- 支持泛型的回傳值
- 需要借助FutureTask類,獲取回傳結果
-
實作步驟
- 創建一個實作Callable的實作類
- 實作call方法,將此執行緒需要執行的操作宣告在call()中
- 創建Callable介面實作類的物件
- 將此Callable介面實作類的物件作為引數傳遞到FutureTask構造器中,創建FutureTask 的物件
- 將FutureTask 的物件作為引數傳遞到Thread類的構造器中,創建Thread物件,呼叫start()
- 獲取Callable中call方法的回傳值
package com.han.createThread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * 創建執行緒的方式三:實作Callable介面 ---jdk5.0 新增 * * @author hlrstart * @create 2021-04-04-11:24 */ //1.創建一個實作Callable的實作類 class NumThread implements Callable{ // 2. 實作call方法,將此執行緒需要執行的操作宣告在call()中 @Override public Object call() throws Exception { int sum=0; for (int i = 1; i <=100 ; i++) { if(i%2==0){ System.out.println(i); sum+=i; } } return sum; } } public class ThreadNEw { public static void main(String[] args) { // 3.創建Callable介面實作類的物件 NumThread num=new NumThread(); //4.將此Callable介面實作類的物件作為引數傳遞到FutureTask構造器中,創建FutureTask 的物件 FutureTask f=new FutureTask(num); //5.將FutureTask 的物件作為引數傳遞到Thread類的構造器中,創建Thread物件,呼叫start() new Thread(f).start(); try { // 6. get()方法的回傳值即為FutureTask構造器引數Callable實作類重寫的call()的回傳值 Object sum= f.get(); System.out.println("總和為:"+sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
新增方式二:使用執行緒池(開發中使用)
-
思想:提前創建好多個執行緒,放入執行緒池,使用時直接獲取,使用完放回池中,
-
好處:
- 提高回應速度(減少創建新執行緒的時間)
- 降低資源消耗(重復利用)
- 便于執行緒管理
-
實作步驟
-
提供指定執行緒數量的執行緒
-
執行指定的執行緒的操作,需要提供實作Runnable介面或Callable介面實作類的物件
class Num implements Runnable{ @Override public void run() { for (int i = 0; i <=100; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class ThreadPool { public static void main(String[] args) { //1.提供指定執行緒數量的執行緒 ExecutorService service = Executors.newFixedThreadPool(10); //2.執行指定的執行緒的操作,需要提供實作Runnable介面或Callable介面實作類的物件 service.execute(new Num()); //適用于Runnable // service.submit(Callable callable); //適用于callable } }
-
4、涉及到的面試題
-
sleep()和wait()的異同?、
相同點:
- 執行該方法,都可以使當前執行緒進入阻塞狀態
不同點:
- 兩個方法的定義位置不同:Thread類中定義sleep(),Object類中定義wait()
- 呼叫的要求不同:sleep()可以在任何需要的場景下呼叫,wait()必須使用在同步代碼塊或同步方法中
- 是否會釋放同步監視器:若兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放(例如睡眠中,廁所門依然沒有打開),wait()會釋放
-
面試題:synchronized 與Lock的異同
相同:
- 都可以解決執行緒安全問題
不同點:
-
synchronized機制在執行完同步代碼之后,自動釋放同步監視器
-
Lock需要手動啟動同步(lock()),同時結束同步也需要手動的實作(unlock())
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/273982.html
標籤:其他
