最近發現,編程這東西,一段時間不用,就差很少忘了,感受腦子永遠不夠用,這下利用點時間整理下思路,記錄下來,已被不時之需。
java
線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。簡而言之:一個程序運行後至少有一個進程,一個進程中能夠包含多個線程。什麼是多線程呢?即就是一個程序中有多個線程在同時執行。編程
單線程程序:即,如有多個任務只能依次執行。當上一個任務執行結束後,下一個任務開始執行。如,去網吧上網,網吧只能讓一我的上網,當這我的下機後,下一我的才能上網。安全
多線程程序:即,如有多個任務能夠同時執行。如,去網吧上網,網吧可以讓多我的同時上網。多線程
分時調度併發
全部線程輪流使用 CPU 的使用權,平均分配每一個線程佔用 CPU 的時間。dom
搶佔式調度編輯器
優先讓優先級高的線程使用 CPU,若是線程的優先級相同,那麼會隨機選擇一個(線程隨機性),Java使用的爲搶佔式調度。ide
大部分操做系統都支持多進程併發運行,如今的操做系統幾乎都支持同時運行多個程序。好比:如今咱們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開着畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,」感受這些軟件好像在同一時刻運行着「。測試
實際上,CPU(中央處理器)使用搶佔式調度模式在多個線程間進行着高速的切換。對於CPU的一個核而言,某個時刻,只能執行一個線程,而 CPU的在多個線程間切換速度相對咱們的感受要快,看上去就是在同一時刻運行。this
其實,多線程程序並不能提升程序的運行速度,但可以提升程序運行效率,讓CPU的使用率更高。
若是有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。
/** * * @author LYJ * 實現Runnable的代碼 * */ public class Ticket implements Runnable { //設置總票數爲100,這裏的ticket是成員變量, //因爲在測試類中new了一次,因此值存在一個,被三個售票窗口共享 int ticket=100; public void run() { //模擬售票 while(true) { //若是票數大於0,繼續售票 if(ticket>0) { //爲了讓線程安全問題效果明顯些,加入線程定時休眠Thread.sleep() try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //Thread.currentThread()是線程獲取當前線程對象的方法 getName()獲取調用者的線程名 System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--); } } } /** * * 開啓多線程的代碼 * */ public class ThreadDemo01 { public static void main(String[] args) { //建立Ticket的Runnable對象 Ticket ticket = new Ticket(); //建立線程3個對象模擬三個售票窗口,並把Runnable對象加入Thread和給Thread命名 new Thread(ticket,"窗口1").start();; new Thread(ticket,"窗口2").start();; new Thread(ticket,"窗口3").start();; }
*****************************************************************
輸出結果:
窗口3正在售票:3
窗口2正在售票:2
窗口1正在售票:1
窗口3正在售票:0
窗口2正在售票:-1
結果中出現了負數和0,這就是線程安全問題,要怎麼解決呢?
加同步鎖 synchronized(Object o){....} o能夠是任意對象
******************************************************************************
加入同步鎖後的代碼
public class Ticket implements Runnable {
//設置總票數爲100,這裏的ticket是成員變量,
//因爲在測試類中new了一次,因此值存在一個,被三個售票窗口共享
int ticket=100;
public void run() {
//模擬售票
while(true) {
//若是票數大於0,繼續售票
//加入同步鎖
synchronized(this) {
if(ticket>0) {
//爲了讓線程安全問題效果明顯些,加入線程定時休眠Thread.sleep()
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Thread.currentThread()是線程獲取當前線程對象的方法 getName()獲取調用者的線程名
System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--);
}
}
}
}
**********************************************
運行幾回,發現運行結果中沒有出現負數和0
同步方法:在方法聲明上加上synchronized
public synchronized void method(){
可能會產生線程安全問題的代碼
}
同步方法中的鎖對象是 this(即調用者對象)
靜態同步方法: 在方法聲明上加上static synchronized
public static synchronized void method(){
可能會產生線程安全問題的代碼
}
靜態同步方法中的鎖對象是 類名.class(由於在加載類文件的時候,靜態同步方法因爲是靜態的也被加載進內存了,類名.class的加載優先級高於靜態方法)
同步代碼塊:在須要同步的代碼外面包上一個synchronized
(Object o){
可能會產生線程安全問題的代碼
}
同步代碼塊中的所對象能夠是任意對象
同步鎖使用的弊端:當線程任務中出現了多個同步(多個鎖)時,若是同步中嵌套了其餘的同步。這時容易引起一種現象:程序出現無限等待,這種現象咱們稱爲死鎖。這種狀況能避免就避免掉。
synchronzied(A鎖){
synchronized(B鎖){
}
}
/** * *建立鎖對象 * */ public class Lock { //這裏用private封裝,爲了避免讓外面隨便造鎖,限制只能有A,B鎖個一把,這樣容易出現死鎖 //即A同窗和B同窗想相互串門,但是沒人只有一把本身房間的鑰匙,並且各自都不肯意先給,因而死鎖 private Lock() {}; public static final Object lockA =new Object(); public static final Object lockB = new Object(); //這裏使用static 爲了讓外界能夠經過類名調用成員變量lockA和lockB //由於外面沒法建立Lock對象,爲了讓外面在不創對象的狀況下調用,加了static,經過類名加變量名訪問 } /** * 線程任務類 * */ import java.util.Random; public class ThreadTask implements Runnable { int x = new Random().nextInt(1);//用隨機數隨機獲取0、1,來模擬CPU隨機分配執行權的行爲 @Override public void run() { while(true) { if(x%2==0) { //狀況一 // 先執行A再執行B:即A同窗先拿了A門的鑰匙去開A門,而後打算開B門 synchronized(Lock.lockA) { System.out.println("A同窗...開A門"); synchronized(Lock.lockB) { System.out.println("A同窗...開B門"); } } }else { //狀況二 // 先執行B執行A:B同窗先拿了B門的鑰匙,去開B門,而後打算開A門 synchronized(Lock.lockB) { System.out.println("B同窗...開B門"); synchronized(Lock.lockA) { System.out.println("B同窗...開A門"); } } } x++; } } /** * * 線程測試類 * */ public class ThreadDemo { public static void main(String[] args) { //建立Runnable的實現類對象 ThreadTask tt = new ThreadTask(); //把Runnable實現類對象加入線程中,建立2個線程 Thread t1 = new Thread(tt); Thread t2 = new Thread(tt); t1.start(); t2.start(); }
*********************************************************
輸出結果:A同窗...開A門
A同窗...開B門
B同窗...開B門
B同窗...開A門
A同窗...開A門
B同窗...開B門
結論:A同窗或者B同窗,一我的前後拿走兩把鑰匙時,線程是正常運行的,一旦A拿了A鎖進去A門的時候,CPU忽然讓B開始執行,讓B拿了B鎖進入B門,結果A須要B鎖,B也須要A鎖,二者又不能後退
因而死鎖現象發生了。
線程之間的通訊:多個線程在處理同一個資源,可是處理的動做(線程的任務)卻不相同。經過必定的手段使各個線程能有效的利用資源。而這種手段即等待喚醒機制