1、 多線程中的概念:
a. 進程:正在進行中的程序(直譯),其實就是該應用程序在內存中分配的空間。
b. 線程:是在進程中負責程序執行的路徑。負責程序執行的。
在進程中至少有一個線程在執行。
2、什麼是多線程?
當有多部分代碼需要同時運行時,就需要開闢多條執行路徑來完成。這時該程序就是多線程程序。
多線程解決了:讓多部門門代碼同時運行的問題
3、JVM中的多線程瞭解
jvm中也一樣是多線程程序,只要有一個程序負責程序執行,又有一個線程負責着垃圾回收,這個就是同時進行的。
結論:每一個線程都有自己的運行代碼,這個稱之爲線程的任務。
對於每一個線程便於識別都有自己的名稱,比如負責主函數執行程序代碼的線程,稱之爲 主線程,main thread。
主線程運行的代碼都定義在主函數中。
負責回收垃圾的線程:垃圾回收線程。
發現運行結果不一致:多線程的隨機性構成的。因爲cpu的快速切換的原因。
4、 調用run和調用start的區別?
class Demo extends Thread
{
private String name;
Demo(String name)
{
this.name = name;
}
public void run()
{
for(int x=0; x<10; x++)
{
System.out.println("name...."+x+"....."+name);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//創建線程對象。
Demo d1 = new Demo("小強");
Demo d2 = new Demo("旺財");
//開啓線程。讓線程運行起來。
d1.start();
d2.start();
d1.run();
d2.run();
}
}
區別:start():開啓線程的同時,調用run()方法!run():只是在調用run方法,沒有開啓線程!
5、第一種創建線程的方法:繼承Thread類,並複寫run方法。
每一個線程都應該有自己的任務,而且任務都會定義在指定的位置上。
主線程的任務都定義在main方法中。
自定義線程的任務都定義在了run方法中。
Thread t = new Thread();
t.start();//這種開啓只能調用Thread類中自己的run方法。而該run方法中並未定義自定義的內容。
我還需要創建線程,還要讓線程執行自定義的任務。
所以可以複寫run方法。前提必須是繼承Thread類。
而繼承了Thread後,該子類對象就是線程對象。
6、第二種創建線程的方法:
1.實現Runnable接口。
2.覆蓋run方法。
3.通過Thread類創建線程對象。
4.將Runnable接口的子類對象作爲實參傳遞給Thread類中的構造函數。
5.調用start方法開啓線程,並運行Runnable接口子類的run方法。
第二種實現Runnable接口創建線程的思想:
將線程任務和線程對象進行解耦,將線程任務單獨封裝成對象。另,實現Runnable接口可以避免單繼承的侷限性。
所以建議創建多線程,都是用實現Runnable接口方法。
任務對象實現規則,線程對象在使用規則。
7、 線程安全問題:
原因: 1.多線程在同時處理共享數據;
2.線程任務中有多條代碼在操作共享數據。
安全問題成因就是:一個線程在通過多條操作共享數據的過程中,其他線程參與了共享數據的操作,導致了數據的錯誤。
想要知道你的多線程程序有沒有安全問題:只要看線程任務中是否有多條代碼在處理共享數據。
解決:一個線程在通過多條語句操作共享數據的過程中,不允許其他線程參與運算。
Java中提供了同步代碼塊進行引起安全問題的代碼封裝。(同步)
8、 同步:
格式:
synchronized(對象)
{
//需要被同步的代碼;
}
同步:
好處:解決了多線程的安全問題;
弊端:降低了多線程的效率。
同步的前提:
1.至少有兩個線程在同步中;
2.必須保證同步使用的是一個鎖;
9、 同步的第二種表現形式:
同步函數。
問題:同步函數使用的鎖是神什麼?this
10、 同步函數和同步代碼塊的區別?
同步函數使用的固定鎖this
同步代碼塊使用的鎖是可以指定的
11、 靜態同步函數鎖是什麼?
就是所在類的 類名.class 字節碼文件對象。
12、 單例設計模式:懶漢式中加雙重判斷既提高了效率,有增加了安全。
13、 同步的另一個弊端:
容易引發死鎖。
開發時儘量避免同步嵌套的情況。
死鎖演示:
/*
寫一個多線程嵌套的死鎖演示,死鎖就是當兩個鎖同時開啓後,相互爭奪鎖對象而導致的;
*/
class Demo implements Runnable
{
private boolean flag;
Demo(boolean flag)
{
this.flag=flag;
}
public void run()
{
while(true)
{
if(flag)
{
synchronized(MyLock.LOCKA)//LOCKA鎖
{
System.out.println("if locka");
synchronized(MyLock.LOCKB)//LOCKA鎖中嵌套的LOCKB鎖
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.LOCKB)//LOCKB鎖
{
System.out.println("else lockb");
synchronized(MyLock.LOCKA)//LOCKB鎖中嵌套的LOCKA鎖
{
System.out.println("else locka");
}
}
}
}
}
}
class MyLock//定義兩個鎖對象
{
public static final Object LOCKA=new Object();
public static final Object LOCKB=new Object();
}
class DeadLockDemo
{
public static void main(String[] args)
{
/*
創建兩個線程任務,開啓兩個線程
*/
Demo d1=new Demo(true);
Demo d2=new Demo(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
14、 多線程間通信:多個線程處理同一資源,但是處理動作卻不同。
wait(); 可以讓當前線程處於等待,這時的線程被臨時存儲到了線程池中 ;
notify();喚醒線程池中任意一個等待的線程。
notifyAll();喚醒線程池中所有的等待線程。
這些方法在使用時,必須定義在同步中,必須被所屬同步的鎖調用。
15、 單生產者、單消費者:等待喚醒機制。
16、 多生產者,多消費者:
17、 sleep和wait的區別?
sleep必須指定時間,wait可以指定可以不指定。
sleep和wait都可以讓線程處於凍結狀態,釋放執行權
持有鎖的線程執行sleep,,不釋放鎖,持有鎖的線程執行到wait釋放鎖。
sleep到時間會自動醒,wait沒有指定時間,只能被其他線程通過notify喚醒。
------------------------------------------------------------------------
Lock lock=new ReentrantLock();創建互斥鎖
await();等待
signal();喚醒
signalAll();喚醒所有