/**
* 程序 :
* 就是指我們寫的保存硬盤當中的靜態文件,叫程序
* 進程 :
* 就是程序被加載進內存,準備或者正在運行時,就叫進程
* 線程 :
* 就是程序中不同的執行路徑
*
* 線程 :
* 當我們程序開始執行的時候,就是java中main()方法開始執行,在棧內存中開辟main()棧幀的時候,就是開啟了一個線程
* 一個線程就是一個棧及其里面的鏈式棧幀,一個棧幀就對應一個方法.
*
*/
☆☆☆☆ 重點 ☆☆☆☆ !
創建一個新的線程有兩種方式,
但是啟動一個線程,只有一種方式(唯一的一種);
//三個要點 :
線程的定義;
線程的啟動;
線程開啟之后;
// 線程的定義 :
程序不同的執行路徑,從代碼角度來講,線程就是一個類,從內存角度來講,線程就是一個棧和鏈式棧幀
// 線程必須要先啟動 :
start();//成員方法
// 一旦開啟多線程 :
這個時候,說代碼是從左到右,從上往下執行這句話就不準確了,因為,我們開啟了兩個線程,在每個線程的內部,仍然是從上到下,從左到右,但是線程之間的代碼,沒有固定的先后順序,他們執行的先后順序,是不確定的,和CPU,操作系統,和我們自己的設定,都相關
Thread :
//創建線程
創建一個新的線程,有兩個方式 :
//第一種 :
第1步 : 繼承java.lang.Thread;這個類
第2步 : 重寫run()方法,為什么要重寫?因為run()就相當于新線程的main()方法,就是程序執行的起點和終點;
//創建了線程,不啟動,不能用 : start();
//開啟線程 :
Thread t = new 子類名();
t.start();//就開啟線程了
Runnable :
//第二種 :
第1步 : 實現java.lang.Reunnable接口
第2步 : 實現接口的run()方法;
//開啟線程 :
1 Thread t = new Thread(new 子類名());
t.start();//開啟線程
2 Runnable(或者是子類名) r = new 子類名();//多態
Thread t = new Thread(r);
t.start();//開啟線程
Thread_start :
start();//啟動線程(唯一的方法) 默認會去調用該線程的run()方法 成員方法
啟動一個線程 : 只能是Thread類中的start()方法;
currentThread :
currentThread();//獲取當前線程的內存地址 靜態方法,類名調用就行 對象名也行,底層會默認轉換為類名
Thread.currentThread();//獲取當前線程的內存地址(在哪個線程寫的,Thread就是哪個線程)
setName :
setName();//給線程取名字,不設置默認是Thread-0,依次累加
Thread t1 = new 子類名();//默認的名字是在new的時候取得 Thread-0
t1.setName("t1");//現在,線程名字就改為t1了
t1.start();
getName :
getName();//獲得當前線程的名字 成員方法
Thread.currentThread().getName();//因為getName()是成員方法,所以想調用這個方法,要先獲得當前線程的內存地址吧,就是Thread.currentThread();
sleep :
sleep : 在那個線程寫的,就鎖定那個線程,和 用哪個線程的引用調用sleep()沒有關系(因為底層也會默認轉成類名)
//讓某個線程休眠 可以直接類名調用就行了
//線程中,加了sleep(),就必須拋異常或者捕捉異常
sleep();//讓當前線程睡眠指定的毫秒數 是靜態方法 類名調用就行
Thread.sleep(1000);//讓該線程睡眠 1 秒;寫在那個線程,就睡眠那個線程
setPRiority :
setPriority();//設置線程的優先級 成員方法
線程的默認優先級 :
//子線程默認繼承父類的線程優先級
java中線程的優先級有0-10 11個級別
win當中是1-7 7個優先級
MIN_PRIORITY : 1 //最低
MAX_PRIORITY : 10 //最大
NORM_PRIORITY : 5 //正常
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);//(設置當前線程的優先級)成員方法要用對象引用去調用吧.先獲取當前線程的內存地址,調用setPriority();去設置優先級 這里設置的為 1 最低的 不設置默認為 5 NORM_PRIORITY
Thread t = new 子類名();
t.setPriority(Thread.MAX_PRIORITY);//這和上面那個是一樣的,只不過上面那個,寫在哪,就設置那個線程的優先級,這個是設置指定的那個線程的優先級
//喚醒休眠的兩種方法 :
1 Interrup();//異常喚醒,需要捕捉和拋異常
2 while();//循環
Interrup :
Interrup();//這種方式喚醒,一定要try...catch..處理異常或者是throws拋異常
//異常類型 : InterruptedException
Thread t = new 子類名();//這種是繼承Thread并覆寫run()方法的,這種創建線程方式
Thread t = new Thread(new 子類名());//這種是實現Runnable接口,并實現run()方法的,這種創建線程方式
t.start();//啟動線程
t.interrupt();//強行喚醒(異常喚醒)
while :
用while循環(或者別的循環,加if),
需要強行喚醒,就在主線線程設置 引用(對象名).run=false;就行了
boolean run = true;//成員變量
int i =0;//成員變量
public void run(){//線程類的run()方法
while(run){//循環 這里如果為true 就會一直循環(死循環把)
i++;
MIN_PRIORITY : 1
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName()+"--->"+i );
}
}
join :
join();//也就是線程的合并,把兩個線程,合并成一個線程(成了單線程) 成員方法
等著當前線程執行完畢,然后再在本線程后面繼續往下執行,當前行以后,所有的main線程的代碼,都必須等著這個線程執行完畢以后再執行,相當于這個線程和main線程合并,變為單線程程序,又是從上往下執行
Thread t = new 子類名();
t.start();//啟動線程
t.join();//這行一下的main線程的代碼,都必須等待t執行完,才能繼續執行
yield :
yield();//靜態方法 可以直接類名調用,讓當前線程讓位一次(只跟同級)
1 這是一個靜態方法,一維這:即使用對象調用他,也會在底層轉換為類名調用,停掉的也還是當前所在的線程,也就是說在那個類中調用,就停止那個線程
2 給同一個優先級的線程讓位,不同的優先級,不讓位
3 和sleep()方法相同,那個線程調用就停止那和累的線程,但是沒有時間,不固定,只是讓出當前這個時間片,下一個時間片讓不讓,另說
Thread.yield();//跟同級線程,讓位一次 寫在哪,哪個就讓位一次
synchronized: :
synchronized : 并不是往對象里面加鎖,鎖是每個對象都有的屬性,synchronized只是鎖上了這把鎖,是個持續的動作而已
synchronized 是一個修飾符,修飾的方法,等于是給方法加了一把鑰匙,當一個線程執行這個方法的時候,別的所有線程都不能再去執行這個方法,和用synchronized修飾的方法,一個方法鎖住,所有用synchronized修飾的方法都會鎖上,但是沒有用synchronized修飾的方法,可以訪問//目的就是讓數據同步
//線程同步本質 :
當多個線程(服務,任務),操作同一個數據的時候,保證數據一致,本質就是數據同步,是一種數據安全機制
這種機制,操作上更多是從數據上來保護的
//異步編程模型 :
線程是完全獨立的,誰的運行也不會受到別的線程的影響
//同步編程模型 :
線程間不是獨立的,互相間有影響的,某些線程必須單獨完成任務以后,才能 夠讓別的線程執行(為什么?)
//同步的原因 :
1 同步是指數據的同步,為了數據安全,必須要等某個線程對數據操作完成以后,在引入別的線程進行操作
同步機制,某種程度是多線程編程變成了單線程
2 同步的環境
1 必須多線程
2 多個線程有同時操作同一個數據的可能性
3 主要就是涉及數據更改的操作
線程同步是數據的概念
//方法 : 鎖定,鎖定是一個邏輯上的概念,是為了保證同步的手段
//方發鎖 : 按照封裝性,根本不可能再是直接就能對數據進行操作只能通過方法進行操作
//對象鎖 : 堆內存,只要一加鎖,誰也進不來
//類鎖 : 更大級別的
只要加了 synchronized 修飾的成員方法(靜態方法),是多個線程不可以用加了synchronized的方法同時訪問這個對象(類)
同一個方法互斥,不同的方法也互斥
//靜態方法聲明 :
又稱為類鎖;
public synchronized static void m1(){
System.out.println( "不管誰被鎖,我都會被鎖,他解開,我才解開" );
}
public synchronized static void m2(){
System.out.println( "不管誰被鎖,我都會被鎖,它解開,我才解開" );
}
public static void m3(){
System.out.println( "別人鎖不鎖,干我何事" );
}
比如我有兩個線程,t1和t2把
t1調用了m1();/*就會鎖住*/ t2就不能調用m1()和m2();/*m2()也會被鎖住*/只能調用m3();//因為m3()沒有用synchronized修飾
//成員方法聲明 :
又稱對象鎖
前提是/*對象*/必須是同一個對象
public synchronized void m1(){
System.out.println( "不管誰被鎖,我都會被鎖,他解開,我才解開" );
}
public synchronized void m2(){
System.out.println( "不管誰被鎖,我都會被鎖,它解開,我才解開" );
}
public void m3(){
System.out.println( "別人鎖不鎖,干我何事" );
}
比如我有兩個線程,t1和t2把//并且t1線程和t2線程都是同一個對象
t1調用了m1();/*就會鎖住*/ t2就不能調用m1()和m2();/*m2()也會被鎖住*/只能調用m3();//因為m3()沒有用synchronized修飾
setDaemon :
setDaemon();//設置守護線程(兜底線程)
只要主線程執行完畢,不管守護線程執行了多少,執行到哪,都不會在執行,也跟著死去,但是跟主線程晚結束一下,
//活,他先活,死,他先死(要么怎么守護,守護線程,先活,后死)
Thread t1 = new 子類名();//創建一個線程把,用的是繼承的方式
t1.setDaemon(true);//里面是布爾型 true/false 把當前線程設置為守護線程
t1.start();//啟動t1線程把
//如果我守護線程里面有個死循環(就是不會結束的那種),但是,只要主線程執行完畢,守護線程也會跟著結束
Timer :
1 創建一個定時器(Timer)
2 創建一個執行定時器任務的類,這個類必須繼承于 TimerTask 這個類,并且,必須覆寫 TimerTask 類中的 run(),方法.(TimerTask)
3 用 Timer 類里面的 schedule()方法,分配并執行定時器任務(schedule)
Timer :
Timer t = new Timer();//創建一個定時器,并且調用Timer類里面的 schedule()方法,分配執行的任務
TimerTask :
class 類名 extends TimerTask{
public void run(){
System.out.println( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) );//獲取當前時間,輸出
}
}
Date :
Date d = new Date();//獲取當前的時間
String d1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(d);//以這種形式,輸出當前時間d
schedule :
schedule();//Timer里面的schedule(要執行任務的類對象 , 起始的時間 , 任務執行間隔的毫秒數 );//循環執行方法,默認調用run()方法
t.schedule(
new 類名(),/*執行定時器任務的類的對象*/
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2015-09-10 14:48:00 000"),/*其實時間(從什么時間,開始執行)*/
1000*3)/*執行任務的間隔時間*/;
synchronized :
//死鎖
synchronized : 并不是往對象里面加鎖,鎖是每個對象都有的屬性,synchronized只是鎖上了這把鎖,是個持續的動作而已
死鎖 : 死鎖就是大家都訪問不了的狀態;
原理 :
1 兩個線程,兩個對象
2 先后 嵌套 鎖定 執行 兩個對象(先鎖定第一個對象,再嵌套鎖定第二個對象)
3 先后 嵌套 鎖定 執行 兩個對象(先鎖定第二個對象,再嵌套鎖定第一個對象)
4 在第一個線程執行過程中,執行到第二個對象的時候,發現第二個被鎖死了,也只能等待
5 第二個線程執行過程中,執行到第一個對象的時候,發現第一個被鎖死了,也只能等待
public class Thread線程{
public static void main(String[] args){
//這里我用Object是因為不用再寫實體類了,這個類可以自己寫
Object o1 = new Object();//第一個對象
Object o2 = new Object();//第二個對象
Thread t1 = new Thread(new T1(o1,o2));//第一個線程(調用的是有參構造把)
Thread t2 = new Thread(new T2(o1,o2));//第二個線程
t1.start();//啟動線程
t2.start();//啟動線程
}
}
class T1 implements Runnable{//線程類吧
Object o1;//成員變量 這里用于接收實參傳進來的Object的對象引用(實體類用的那個類,這里就要用那個類去接吧)
Object o2;
T1(Object o1,Object o2){//有參構造
this.o1 = o1;//賦值
this.o2 = o2;
}
public void run(){//覆寫的run()方法
synchronized(o1){//鎖定第一個對象 (o1)
try{
Thread.sleep(1000);//不睡眠一秒,有時候不死鎖,睡眠一秒,百分百死鎖(因為如果是我t1線程執行完了,t2線程在開始執行的呢?那就不是死鎖了吧,這樣,t1到這停一秒,一秒足夠t2也走的o2的那個對象那里,所以就死鎖了,都在等待)
}catch(Exception e){
e.printStackTrace();
}
synchronized(o2){//在鎖定第一個對象(o1)中 嵌套鎖定第二個對象(o2)
System.out.println( "t1" );//如果,t1執行完成了,就會輸出 但是如果死鎖了,t1就進不來吧 就不會輸出
}
}
}
}
class T2 implements Runnable{//創建線程類
Object o1;//成員變量 這里用于接收實參傳進來的Object的對象引用(實體類用的那個類,這里就要用那個類去接吧)
Object o2;
T2(Object o1, Object o2){//和上面一樣,不懂就看上面的注釋
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized(o2){//t1線程先鎖定的o1,我這里如果先鎖定o2,沒有關系吧,t1能進o1,但是我也能進o2 ,因為是不同的對象,但是我如果再想進o1,是不是就進不去了(因為t1進去就鎖了)
try{
Thread.sleep(1000);//這個也停一秒,就是當t2進來o2這個對象,并鎖定了,t1也肯定就能進去o1這個對象,并也鎖定了,現在就開始進入等待了吧,就是死鎖
}catch(Exception e){
e.printStackTrace();
}
synchronized(o1){//嵌套鎖定o1
System.out.println( "t2" );
}
}
}
}
wait :
wait是 Object 類的方法 ,不是Thread中的方法 Thread中wait也是繼承于 Object
注意 : 如果該對象沒有被鎖定,則調用wait方法,就會報錯,即只有在同步方法或者同步代碼塊中(也就是某線程訪問,用 synchronized 修飾的方法之后)才可以調用wait方法,notify,notifyAll同理
this.wait();不是讓當前對象wait,而是讓當前鎖定this對象的線程wait,同時 釋放對this的鎖定
//wait()和sleep()的區別 :
wait : wait()會把鎖打開,讓別的線程進來,并且自身進入睡眠,只能等待喚醒,如果不喚醒,會一直休眠(并且,當前線程wait()下面的代碼不會執行,只能等待喚醒,才能執行)
sleep : sleep()就算設置了休眠,但是鎖也不會打開,別的線程也進不來,并且可以喚醒,但是如果不喚醒,到了指定的睡眠時間,自身也會喚醒(并且,當前線程sleep()下面的代碼不會執行,只能等待喚醒,才能執行)
this.wait();//如果這個休眠,會打開鎖,讓別的線程執行
System.out.println( "我在睡眠" );//不會執行,如果沒有線程喚醒,永遠不會執行,會永遠睡眠
Thread.sleep(5000);//睡眠5秒,并且不會打開鎖,別的線程也得等著,一樣進不來
System.out.println( "我快喚醒了" );//如果沒有線程喚醒當前睡眠的線程,那么,5秒后,自動醒,一樣會輸出
notify :
notify是Object 類的方法 ,不是Thread中的方法 Thread中notify也是繼承自Object
注意 : 如果該對象沒有被鎖定,則調用wait方法,就會報錯,即只有在同步方法或者同步代碼塊中(也就是某線程訪問,用 synchronized 修飾的方法之后)才可以調用wait方法,notify,notifyAll同理
notify() : 隨機喚醒一個在該對象上睡眠的某一個線程(一般都是誰先睡眠,誰先醒,具體不知道,是系統隨機喚醒的),并且,沒有讓自身進入睡眠狀態
this.notify();//隨機喚醒一個在該對象上睡眠的線程
System.out.println( "我能輸出" );//我能輸出,因為上面只是喚醒了一個線程,并沒有讓自身進入睡眠狀態
notifyAll :
notifyAll是Object 類的方法 ,不是Thread中的方法 Thread中notifyAll也是繼承自Object
注意 : 如果該對象沒有被鎖定,則調用wait方法,就會報錯,即只有在同步方法或者同步代碼塊中(也就是某線程訪問,用 synchronized 修飾的方法之后)才可以調用wait方法,notify,notifyAll同理
notifyAll() : 喚醒在該對象上,所有等待的線程,并且,沒有讓自身進入睡眠狀態
this.notifyAll();//喚醒所有在該對象上睡眠的線程
System.out.println( "我能輸出" );//我能輸出,因為上面只是喚醒了所有線程,并沒有讓自身進入睡眠狀態
新聞熱點
疑難解答