線程
? ? ? ? 線程是進程中的一個單一順序的執行單元,也被稱為輕量進程(lightweight process)。線程有自己的必須資源,同時與其他線程進程共享全部資源。同一個進程中,線程是并發執行的。由于線程的之間的相互制約,線程有就緒、阻塞、運行、結束等狀態。
一 新建線程
我們先創建兩個線程類:
新建一個MyThread類 繼承 Thread來覆蓋 run方法:
新建MyRunnable類 實現Runnable 接口:
創建以及執行兩個線程類
二 驗證線程并發
在上述創建的MyThread類和MyRunnable類各自的run方法輸出上 加個for循環for(int i=0;i<10000;i++);
如果是順序執行,應該會是 extendsThread全部輸出完 ,才輸出implement thread。但是這并不是而是出現交叉結果 證明線程是并發執行。
交叉原理就是每個代碼 在cpu時間片內執行一段時間,到時間就換一個代碼 在cpu時間片內執行,但是如果時間片內打印完了,就看不見交叉現象。
三 線程聲明周期(或者線程狀態)
java中線程基本上有以下圖片中的幾種狀態:
首先我們要做一個輸出線程狀態的類:
線程狀態
public static void printThreadState(Thread thread){
Thread.State state=thread.getState();
switch(state){
//1.新建狀態
caseNEW:
info(thread.getName()+" state is NEW");break;
//2.執行狀態
case RUNNABLE:
info(thread.getName()+" state is RUNNABLE");break;
//3.等待超時狀態
case TIMED_WAITING:
console.info(thread.getName()+" state is TIMED_WAITING");break;
//4.等待狀態
case WAITING:
info(thread.getName()+" state is WAITING");break;
//5.阻塞狀態
case BLOCKED:
info(thread.getName()+" state is BLOCKED");break;
//6.結束狀態
caseTERMINATED:
info(thread.getName()+" state is TERMINATED");break;
default:
info
(thread.getName()+" state is UNKOWN");
}
}
//打印
public static? void info(Object o){
System.out.println(String.valueOf(o));
}
1.新建狀態:
new之后 start之前都屬于NEW狀態,一旦狀態變成其他,則不會再變成NEW狀態。
public static? void main(String args[]) throws InterruptedException{
Thread t=new Thread(new Runnable() {
@Override
public void run() {
}
});
printThreadState(t);//查看new之后的thread狀態
}
2.執行狀態
在run()方法里的時候,線程處于執行狀態。一旦run()方法執行完畢,則狀態就不是執行狀態了
3.等待超時狀態
當調用wait(long timeout)方法的時候,線程處于等待狀態,并且超時后并不馬上返回,而是等待獲取鎖后返回。
驗證超時后并不馬上返回,而是等待獲取鎖后返回。
所以我們會發現一個問題,wait的超時只是一個預想, 多少時間后我再去獲取鎖,如果拿到就返回 拿不到就處于等待狀態。這里就會發現這里等待是最少需要long timeout時間。
wait(等多少時間后 再去競爭鎖),wait的時間內 我不去競爭 跟別人搶鎖,所以這個時間 不保證鎖一定能搶回來。
注意:Thread.sleep 也會進入TIME_WAITING狀態。
5.等待狀態
wait()睡眠不參與搶鎖,等待notify來進行喚醒。
wait 也可以通過interrupt喚醒,interrupt是中斷線程的方法。
5.阻塞狀態
如果獲取不到鎖,就一直在阻塞狀態,直到獲取鎖為止。
6.結束狀態
在run()方法執行結束以后,線程處于結束狀態
但是當這個線程執行結束了之后,是否代表這個線程被銷毀了呢。然而只是線程執行單元銷毀了,但是線程對象還在。雖然這個線程對象還存在,但是它已經不能進行再次的start()方法進行執行了。
四 研究多線程鎖問題
經典并發問題:當運行一萬個線程想List插入的時候,預期結果是有10000個,但是輸出確不是10000個,而是少于1000個。原因是 線程并發執行獲取list的長度是過期的。
比如我拿到list ,你也拿到list ,咱倆同時看list里有1條數據 我把數據插入第二個位置 ,你也插入第二個位置 ,就會導致list最終結果只有2條數據,實際上插入了3條數據。
public static void ThreadMuliteTest() throws InterruptedException {
final?List?list=new?ArrayList();
for(int?i=0;i<10000;i++){
new?Thread(new?Runnable()?{
@Override
public?void?run()?{
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
這就是線程并發問題,并發問題有個解決辦法 就是對線程進行枷鎖讓他排隊,我拿到了 你就不能拿。
synchronized 代碼段是java給線程的一個控制權,可以鎖住一個資源,防止被其他人用。加鎖之后線程就進入了 排隊狀態 ,只有一個線程能拿到list的權限 其他等待。
還有一種操作 就是我得到資源后 ,可以釋放一段時間給別人用 ,超時了 我在拿回來自己用。
public static void ThreadGetWait() throws InterruptedException {
Object lock=new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
console.info("線程一:拿到鎖");
try {
lock.wait(100);//釋放100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
console.info("線程一:鎖又被我拿回來了");
try {
Thread.sleep(1000);//睡1s 鎖不給別人
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Thread.sleep(10);//休眠10ms防止第二個線程拿到鎖
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
console.info("線程二:拿到鎖");
try {
Thread.sleep(2000);//拿到鎖后我要休眠2s
} catch (InterruptedException e) {
e.printStackTrace();
}
console.info("線程二:鎖又回來了?");
}
}
}).start();
}
因為線程1先運行 肯定拿到鎖,線程一暫時釋放。所以線程二緊接著拿到了鎖,線程二休眠了2s 期間并沒有釋放鎖,所以線程1一直處于阻塞狀態,線程二執行完成后 釋放鎖 線程一 打印 最后一句。