文章目錄
- 0.基本概念
- 1.建立
- 2.線程的同步
- 3.線程通信
0.基本概念
線程與程序
1.程序(process):是程式(指令集合)的一次執行過成,動态的(的産生、存在和消亡)即生命周期。程序作為資源配置設定的機關,一個程序中的多個線程共享相同的記憶體單元/記憶體位址空間。
2.線程(thread),程序可進一步細化為線程,是一個程式内部的一條執行路徑。線程作為排程和執行的機關,每個線程擁有獨立的運作棧和程式計數器(pc)。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLzElaNFTWq10dRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0IjN4ITM0ITMxIjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
yield():釋放目前cpu的執行權
sleep(long millitime):讓目前線程“睡眠”指定millitime毫秒。在指定的millitime毫秒時間内,目前線程是阻塞狀态。
join():線上程a中調用線程b的join()此時線程a就進入阻塞狀态,直到線程b完全執行完以後,線程a才結束阻塞狀态。
并行與并發
1.并行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。
2.并發:一個CPU(采用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事。但肉眼錯覺,主頻太快看不出交替輪流。
1.建立
方式一:繼承于Thread類
-
建立一個繼承于Thread類的子類
class MyThread extends Thread {
-
重寫Thread類的run() --> 将此線程執行的操作聲明在run()中 PS:由于該類對象調用該線程的重寫方法故這裡Thread.currentThread().getName()等同于this.getName()
}
-
建立Thread類的子類的對象
MyThread t1 = new MyThread();
-
通過此對象調用start():①啟動目前線程 ② 調用目前線程的run()
t1.start();
PS:一個對象智能執行一次start,否則會報IllegalThreadStateException
方式二:實作Runnable接口
-
建立一個實作了Runnable接口的類
class MThread implements Runnable{
-
實作類去實作Runnable中的抽象方法:run()
}
- 建立實作類的對象:MThread mThread = new MThread();
-
将此對象作為參數傳遞到Thread類的構造器中,建立Thread類的對象:
Thread t1 = new Thread(mThread);
t1.setName(“線程一”); //執行個體Thread可以直接帶參"線程一"
- 通過Thread類的對象調用start():① 啟動線程 ②調用目前線程的run()–>調用了Runnable類型的target的run()
比較方式一與方式二:
- 聯系:1. public class Thread implements Runnable;2.兩種方式都需要重寫run(),将線程要執行的邏輯聲明在run()中。
- Runnable接口的方式 1. 實作的方式沒有類的單繼承性的局限性; 2. 實作的方式更适合來處理多個線程有共享資料的情況。
方式三:實作Callable接口
//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;
}
}
//3.建立Callable接口實作類的對象:
NumThread numThread = new NumThread();
//4.将此Callable接口實作類的對象作為傳遞到FutureTask構造器中,建立FutureTask的對象:
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的對象作為參數傳遞到Thread類的構造器中,建立Thread對象,并調用start():
new Thread(futureTask).start();
//6.擷取Callable中call方法的傳回值
//get()傳回值即為FutureTask構造器參數Callable實作類重寫的call()的傳回值:
Object sum = futureTask.get();
比實作Runnable接口建立多線程方式強大:
1.call()可以有傳回值的。
2.call()可以抛出異常,被外面的操作捕獲,擷取異常的資訊
3.Callable是支援泛型的
方式四:使用線程池
-
提供指定線程數量的線程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//設定線程池的屬性
// service1.setCorePoolSize(15);
//service1.setKeepAliveTime();
-
執行指定的線程的操作。需要提供實作Runnable接口或Callable接口實作類的對象
service.execute(Runnalble的實作類對象);//适合适用于Runnable
// service.submit(Callable callable);//适合使用于Callable
-
關閉連接配接池
service.shutdown();
2.線程的同步
方式一:同步代碼塊
synchronized(同步螢幕){
//需要被同步的代碼
}
PS:
1.操作共享資料的代碼,即為需要被同步的代碼。
2.共享資料:多個線程共同操作的變量。–>不能包含代碼多了,也不能包含代碼少了。
3.同步螢幕,俗稱:鎖。任何一個類的對象,都可以充當鎖。要求:多個線程必須要共用同一把鎖。
4.在實作Runnable接口建立多線程的方式中,我們可以考慮使用this充當同步螢幕。在繼承Thread類建立多線程的方式中,慎用this充當同步螢幕,考慮使用目前類(類名.class)充當同步螢幕
方式二:同步方法。
如果操作共享資料的代碼完整的聲明在一個方法中,我們不妨将此方法聲明同步的。
private static synchronized void 方法名(){
}
PS同步方法仍然涉及到同步螢幕,隻是不需要我們顯式的聲明:
非靜态的同步方法,同步螢幕是:this
靜态的同步方法,同步螢幕是:目前類本身
同步機制解決了線程的安全問題。但操作同步代碼時,隻能有一個線程參與,其他線程等待。相當于是一個單線程的過程,效率低。
方式三:Lock鎖
//1.執行個體化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
try{
//2.調用鎖定方法lock()
lock.lock();
//操作共享資料的代碼
...
}finally{
//3.調用解鎖方法:unlock()
lock.unlock();
}
synchronized 與 Lock的異同?
相同:二者都可以解決線程安全問題
不同:1.synchronized機制在執行完相應的同步代碼以後,自動的釋放同步螢幕;2. Lock需要手動的啟動同步(lock()),同時結束同步也需要手動的實作(unlock())
死鎖:同一把鎖(同步螢幕)下的允許線程隻有一個。不同的線程分别占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖。
3.線程通信
涉及的方法:
wait():一旦執行此方法,目前線程就進入阻塞狀态,并釋放同步螢幕。
notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
PS:
1.wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
2.wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步螢幕。否則,會出現IllegalMonitorStateException異常
3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。
面試題:sleep() 和 wait()的異同?
- 相同點:一旦執行方法,都可以使得目前的線程進入阻塞狀态。
- 不同點:1.兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait(); 2.調用的要求不同:sleep()可以在任何需要的場景下調用。wait()必須使用在同步代碼塊或同步方法中;3.關于是否釋放同步螢幕:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖