Java中therad_java中Thread的深入了解(一)
線程
線程是進(jìn)程中的一個(gè)單一順序的執(zhí)行單元,也被稱為輕量進(jìn)程(lightweight process)。線程有自己的必須資源,同時(shí)與其他線程進(jìn)程共享全部資源。同一個(gè)進(jìn)程中,線程是并發(fā)執(zhí)行的。由于線程的之間的相互制約,線程有就緒、阻塞、運(yùn)行、結(jié)束等狀態(tài)。
一 新建線程
我們先創(chuàng)建兩個(gè)線程類:
新建一個(gè)MyThread類 繼承 Thread來覆蓋 run方法:
MyThread類
新建MyRunnable類 實(shí)現(xiàn)Runnable 接口:
MyRunable類
創(chuàng)建以及執(zhí)行兩個(gè)線程類
主線程Main
線程結(jié)果
二 驗(yàn)證線程并發(fā)
在上述創(chuàng)建的MyThread類和MyRunnable類各自的run方法輸出上 加個(gè)for循環(huán)for(int i=0;i<10000;i++);
循環(huán)線程
輸出結(jié)果extends
如果是順序執(zhí)行,應(yīng)該會(huì)是 extendsThread全部輸出完 ,才輸出implement thread。但是這并不是而是出現(xiàn)交叉結(jié)果 證明線程是并發(fā)執(zhí)行。
交叉原理就是每個(gè)代碼 在cpu時(shí)間片內(nèi)執(zhí)行一段時(shí)間,到時(shí)間就換一個(gè)代碼 在cpu時(shí)間片內(nèi)執(zhí)行,但是如果時(shí)間片內(nèi)打印完了,就看不見交叉現(xiàn)象。
單核cpu
三 線程聲明周期(或者線程狀態(tài))
java中線程基本上有以下圖片中的幾種狀態(tài):
線程狀態(tài)
首先我們要做一個(gè)輸出線程狀態(tài)的類:
線程狀態(tài)public static void printThreadState(Thread thread){
Thread.State state=thread.getState();
switch(state){
//1.新建狀態(tài)
caseNEW:
info(thread.getName()+" state is NEW");break;
//2.執(zhí)行狀態(tài)
case RUNNABLE:
info(thread.getName()+" state is RUNNABLE");break;
//3.等待超時(shí)狀態(tài)
case TIMED_WAITING:
console.info(thread.getName()+" state is TIMED_WAITING");break;
//4.等待狀態(tài)
case WAITING:
info(thread.getName()+" state is WAITING");break;
//5.阻塞狀態(tài)
case BLOCKED:
info(thread.getName()+" state is BLOCKED");break;
//6.結(jié)束狀態(tài)
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.新建狀態(tài):
new之后 start之前都屬于NEW狀態(tài),一旦狀態(tài)變成其他,則不會(huì)再變成NEW狀態(tài)。public static? void main(String args[]) throws InterruptedException{
Thread t=new Thread(new Runnable() {
@Override
public void run() {
}
});
printThreadState(t);//查看new之后的thread狀態(tài)
}
2.執(zhí)行狀態(tài)
在run()方法里的時(shí)候,線程處于執(zhí)行狀態(tài)。一旦run()方法執(zhí)行完畢,則狀態(tài)就不是執(zhí)行狀態(tài)了
執(zhí)行狀態(tài)
線程狀態(tài)輸出結(jié)果
3.等待超時(shí)狀態(tài)
當(dāng)調(diào)用wait(long timeout)方法的時(shí)候,線程處于等待狀態(tài),并且超時(shí)后并不馬上返回,而是等待獲取鎖后返回。
查看wait(long timeout)
輸出結(jié)果
驗(yàn)證超時(shí)后并不馬上返回,而是等待獲取鎖后返回。
添加一條輸出結(jié)果
如果是wait(long timeout)方法時(shí)候,提前先把鎖獲取過來等待20s
會(huì)發(fā)現(xiàn)輸出結(jié)果線程還是阻塞狀態(tài)
所以我們會(huì)發(fā)現(xiàn)一個(gè)問題,wait的超時(shí)只是一個(gè)預(yù)想, 多少時(shí)間后我再去獲取鎖,如果拿到就返回 拿不到就處于等待狀態(tài)。這里就會(huì)發(fā)現(xiàn)這里等待是最少需要long timeout時(shí)間。
wait(等多少時(shí)間后 再去競爭鎖),wait的時(shí)間內(nèi) 我不去競爭 跟別人搶鎖,所以這個(gè)時(shí)間 不保證鎖一定能搶回來。
注意:Thread.sleep 也會(huì)進(jìn)入TIME_WAITING狀態(tài)。
5.等待狀態(tài)
wait()睡眠不參與搶鎖,等待notify來進(jìn)行喚醒。
放棄搶鎖,處于waiting狀態(tài)
waiting狀態(tài)時(shí)候喚醒
輸出結(jié)果
wait 也可以通過interrupt喚醒,interrupt是中斷線程的方法。
5.阻塞狀態(tài)
如果獲取不到鎖,就一直在阻塞狀態(tài),直到獲取鎖為止。
把循環(huán)鎖死,當(dāng)狀態(tài)位鎖死的時(shí)候釋放鎖
輸出結(jié)果
6.結(jié)束狀態(tài)
在run()方法執(zhí)行結(jié)束以后,線程處于結(jié)束狀態(tài)
結(jié)束狀態(tài)
但是當(dāng)這個(gè)線程執(zhí)行結(jié)束了之后,是否代表這個(gè)線程被銷毀了呢。然而只是線程執(zhí)行單元銷毀了,但是線程對象還在。雖然這個(gè)線程對象還存在,但是它已經(jīng)不能進(jìn)行再次的start()方法進(jìn)行執(zhí)行了。
四 研究多線程鎖問題
經(jīng)典并發(fā)問題:當(dāng)運(yùn)行一萬個(gè)線程想List插入的時(shí)候,預(yù)期結(jié)果是有10000個(gè),但是輸出確不是10000個(gè),而是少于1000個(gè)。原因是 線程并發(fā)執(zhí)行獲取list的長度是過期的。
比如我拿到list ,你也拿到list ,咱倆同時(shí)看list里有1條數(shù)據(jù) 我把數(shù)據(jù)插入第二個(gè)位置 ,你也插入第二個(gè)位置 ,就會(huì)導(dǎo)致list最終結(jié)果只有2條數(shù)據(jù),實(shí)際上插入了3條數(shù)據(jù)。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());
}
輸出結(jié)果
這就是線程并發(fā)問題,并發(fā)問題有個(gè)解決辦法 就是對線程進(jìn)行枷鎖讓他排隊(duì),我拿到了 你就不能拿。
synchronized 代碼段是java給線程的一個(gè)控制權(quán),可以鎖住一個(gè)資源,防止被其他人用。加鎖之后線程就進(jìn)入了 排隊(duì)狀態(tài) ,只有一個(gè)線程能拿到list的權(quán)限 其他等待。
還有一種操作 就是我得到資源后 ,可以釋放一段時(shí)間給別人用 ,超時(shí)了 我在拿回來自己用。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防止第二個(gè)線程拿到鎖
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();
}
輸出結(jié)果
因?yàn)榫€程1先運(yùn)行 肯定拿到鎖,線程一暫時(shí)釋放。所以線程二緊接著拿到了鎖,線程二休眠了2s 期間并沒有釋放鎖,所以線程1一直處于阻塞狀態(tài),線程二執(zhí)行完成后 釋放鎖 線程一 打印 最后一句。
總結(jié)
以上是生活随笔為你收集整理的Java中therad_java中Thread的深入了解(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java模块是什么6_Java 9 揭秘
- 下一篇: 二叉树的三叉链表实现c语言,数据结构:二