java執行緒(網易大資料面試題)
阿新 • • 發佈:2021-11-23
網易大資料面試題目
- 執行緒的實現方式有什麼,寫出來
- 講一下synchronize和volatile鎖的問題
一、執行緒的實現方式
1.繼承Thread類(建立Thread類的匿名子類)
- 自定義類繼承Thread類
- 重寫run方法
- new一個自定義類,呼叫start方法
//1. 繼承類的方式建立執行緒,資料未共享 Thread atm1 = new ATM(); Thread atm2 = new ATM(); atm2.start(); atm1.start(); class ATM extends Thread{ private int money = 100; @Override public void run() { while (money>=0){ System.out.println(Thread.currentThread().getName()+"-繼承Thread類的atm:"+money--); } } }
2.實現Runnable介面
- 自定義類實現Runnable介面
- 實現run方法
- new一個自定義類,作為引數傳遞到Thread類構造器中,new一個Thread類
- 呼叫Thread類的start方法
兩種方式對比
- 優先選用Runnable,不需繼承,避免了繼承一個類的侷限性
- 適合處理共享資料(不是說繼承類不能處理共享資料,用代理類(單例模式+代理)也可以輕鬆處理共享資料)
//2. 實現Runnable介面的方式建立執行緒,資料共享 ATM1 atm3 = new ATM1(); Thread thread1 = new Thread(atm3); Thread thread2 = new Thread(atm3); thread1.start(); thread2.start(); class ATM1 implements Runnable{ private int money = 100; @Override public void run() { while (money>0){ // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } synchronized (this){ System.out.println(Thread.currentThread().getName()+"-實現介面的atm:"+money--); } } } }
3.實現Callable介面
- 實現Callable介面,重寫call方法
- new一個自定義類,作為引數傳入FutureTask類構造器,new一個FutureTask物件
- FutureTask物件作為引數傳入Thread類構造器,new一個Thread物件
- 呼叫start方法
//3. 實現Callable介面的方式建立執行緒 ATM2 atm2 = new ATM2(); FutureTask futureTask = new FutureTask<>(atm2); Thread thread = new Thread(futureTask); thread.start(); class ATM2 implements Callable{ private int money = 100; @Override public Object call() { while (money>=0){ System.out.println(Thread.currentThread().getName()+"-實現Callable介面的atm:"+money--); } return null; } }
4.執行緒池建立執行緒
- Executors類建立一個執行緒池物件
- 設定執行緒池屬性
- 呼叫執行緒池的submit方法(含run的物件)或commit方法(含call的物件),
- 關閉執行緒池
// 4. 執行緒池建立執行緒
// 建立一個執行緒池
ExecutorService service = Executors.newFixedThreadPool(10);
//設定執行緒池的屬性
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service1.setCorePoolSize(15);
service.execute(new ATM());//適合用於Runnable和繼承Thread類
// service.submit();//適合用於Callable
service.shutdown();
Thread類常用方法
currentThread:靜態方法,返回當前執行程式碼的執行緒(Thread類的物件)
setName():為執行緒設定名字
getName():獲取當前執行緒名字
start(): 啟動執行緒
sleep(): 靜態方法,讓當前執行緒沉睡 毫秒
yield()釋放當前cpu的執行權
join():線上程a中呼叫執行緒b的join(),此時執行緒a就進入阻塞狀態,直到b完全執行後,a才結束阻塞狀態
二、執行緒的同步
1.synchronized
- 同步程式碼塊 synchronized(同步監視器){}
- 同步方法(方法中只包含處理共享資料的程式碼)
- 同步監視器,俗稱鎖,多個執行緒必須共用一把鎖,任何一個類的物件都可以充當鎖。
- 同步方法,仍需同步監視器,只是未顯式宣告。
- 非靜態的同步方法,同步監視器是this
- 靜態的同步方法,同步監視器是:當前類本身(為Class的物件)
2、lock鎖
- 新建一個lock鎖
- 讀寫共享資料之前呼叫lock方法
- 結束時呼叫unlock方法,注意多個執行緒用同一把鎖。
//lock鎖
ATM3 atm3 = new ATM3();
ProxyThread proxyThread1 = new ProxyThread(atm3);
ProxyThread proxyThread2 = new ProxyThread(atm3);
ProxyThread proxyThread3 = new ProxyThread(atm3);
proxyThread1.start();
proxyThread2.start();
proxyThread3.start();
class ATM3 {
private int money = 100;
private Lock lock = new ReentrantLock();
public void modMoney() throws InterruptedException {
// Thread.sleep(1000);
while (true){
lock.lock();
if(money>=0)
System.out.println(Thread.currentThread().getName()+"-繼承Thread類的atm:"+money--);
lock.unlock();
}
}
}
class ProxyThread extends Thread{
private ATM3 atm ;
ProxyThread(ATM3 atm){
this.atm = atm;
}
@Override
public void run() {
try {
atm.modMoney();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、執行緒的排程
- 排程策略
-
時間片
-
搶佔式:高優先順序的執行緒搶佔cpu
- java的排程方法
- 同優先順序執行緒組成先進先出佇列,使用時間片策略
- 對高優先順序,使用優先排程的搶佔式策略
執行緒的優先順序
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 --預設優先順序
獲取和設定執行緒優先順序
1. getPriority 獲取執行緒優先順序
2. setPriority設定執行緒優先順序
高優先順序的執行緒要搶佔低優先順序執行緒cpu的執行權,但只是從概率上講,並不意味著只有當高優先順序的執行緒執行完後,低優先順序的執行緒才執行
四、執行緒的生命週期
JDK用Thread.State類定義了執行緒的幾種狀態
新建、就緒、阻塞、執行、死亡
五、執行緒通訊
- wait() 使呼叫執行緒進入阻塞狀態,並釋放鎖(與sleep不同)
- notify() 喚醒一個含有wait()的執行緒(優先順序最高的),
- notifyAll():喚醒所有含有wait()的執行緒
說明:
- 上述三個方法必須使用在同步程式碼塊或同步方法中(lock不行)
- 這三個方法的呼叫者必須是同步程式碼塊或同步方法中的同步監視器,否則出現異常
- 上述三個方法定義在java.lang.Object類中
sleep()和wait()的異同
1. 相同點:一旦執行方法,都可以使得當前執行緒進入阻塞狀態
2. 不同點:
1. 兩個方法宣告的位置不同:Thread類中宣告sleep(),Object類中宣告wait()
2. 呼叫的要求不同:sleep()可以在任何需要的場景下呼叫。wait()必須使用在同步程式碼塊中
3. 關於是否釋放同步監視器:sleep()不釋放,wait()釋放