线程、同步
?
目錄
一、多線程
1.多線程的原理
2.線程類Thread的介紹
3.創(chuàng)建線程的兩種方式
4.兩種創(chuàng)建線程方式的區(qū)別
二、線程安全
1.線程安全問題出現(xiàn)的原因
2.線程同步
(1)同步代碼塊
(2)同步方法
(3)Lock鎖
三、線程的狀態(tài)
1.線程的六種狀態(tài)
四、代碼練習(xí)
1.賣包子案例
2.過山洞案例
一、多線程
1.多線程的原理
同一時間內(nèi),CPU只能處理1條線程,只有1條線程在工作(執(zhí)行);多線程并發(fā)(同時)執(zhí)行,其實是CPU快速地在多條線程之間調(diào)度(切換)。如果CPU調(diào)度線程的時間足夠快,就造成了多線程并發(fā)執(zhí)行的假象。
程序啟動運行main時候,java虛擬機啟動一個進程,其中有一個主線程(main方法調(diào)用時候被創(chuàng)建)。隨著調(diào)用Thread對象的start方法,另外一個新的線程也啟動了,這樣,整個應(yīng)用就在多線程下運行。
2.線程類Thread的介紹
構(gòu)造方法:
public Thread() :分配一個新的線程對象。
public Thread(String name) :分配一個指定名字的新的線程對象。
public Thread(Runnable target) :分配一個帶有指定目標(biāo)新的線程對象。
public Thread(Runnable target,String name) :分配一個指定目標(biāo)新的線程對象并指定名字。
常用方法:
public String getName() :獲取當(dāng)前線程名稱。
public void start() :導(dǎo)致此線程開始執(zhí)行; Java虛擬機調(diào)用此線程的run方法。
public void run() :此線程要執(zhí)行的任務(wù)在此處定義代碼。
public static void sleep(long millis) :使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行)。
public static Thread currentThread() :返回對當(dāng)前正在執(zhí)行的線程對象的引用。
3.創(chuàng)建線程的兩種方式
方式一:
? 描述:
將一個類聲明為一個Thread的子類。
這個子類應(yīng)該重寫run類的方法,然后可以分配并啟動子類的實例
步驟:
a.創(chuàng)建子類 繼承 Threand (子類也是一個代表線程的類)
b.子類中重寫run方法(編寫任務(wù)代碼的地方)
c.創(chuàng)建子類對象(創(chuàng)建一個線程對象)
d.啟動線程,調(diào)用線程對象.start();
注意:
啟動線程必須調(diào)用start,而不是調(diào)用run
方式二:
描述:
實現(xiàn)Runnable接口方式
步驟:
a.定義Runnable接口的實現(xiàn)類,并重寫該接口的run()方法
b.用public Thread(Runnable target)的構(gòu)造方法創(chuàng)建Thread對象
c.啟動線程,調(diào)用線程對象.start();
4.兩種創(chuàng)建線程方式的區(qū)別
實現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢:
(1)可以避免java中的單繼承的局限性。
(2)實現(xiàn)解耦操作,任務(wù)可以被多個線程共享,任務(wù)和線程獨立。
(3)線程池只能放入實現(xiàn)Runable或Callable類線程,不能直接放入繼承Thread的類。
5.使用匿名內(nèi)部類創(chuàng)建線程
使用匿名內(nèi)部類的方式實現(xiàn)Runnable接口,重新Runnable接口中的run方法
二、線程安全
1.線程安全問題出現(xiàn)的原因
當(dāng)有多個線程在同時運行,這些線程同時運行一段代碼(同一段任務(wù)代碼,同一個run方法),操作同一個共享數(shù)據(jù)時,這時候可能就會出現(xiàn)線程的安全問題,即線程不安全的。
2.線程同步
使線程按照一定的先后次序進行運行
(1)同步代碼塊
synchronized(同步鎖){
需要同步操作的代碼
}
對象的同步鎖只是一個概念,可以想象為在對象上標(biāo)記了一個鎖.
①鎖對象 可以是任意類型。
②多個線程對象 要使用同一把鎖。
(2)同步方法
public synchronized void method(){
可能會產(chǎn)生線程安全問題的代碼
}
對于非static方法,同步鎖就是this。
對于static方法,我們使用當(dāng)前方法所在類的字節(jié)碼對象(類名.class)。
(3)Lock鎖
java.util.concurrent.locks.Lock 機制提供了比synchronized代碼塊和synchronized方法更廣泛的鎖定操作,同步代碼塊/同步方法具有的功能Lock都有,除此之外更強大,更體現(xiàn)面向?qū)ο蟆?/p>
Lock鎖也稱同步鎖,加鎖與釋放鎖方法化了,如下:
public void lock() :加同步鎖。
public void unlock()?:釋放同步鎖。
?
?????Lock l = ...;
?????l.lock();
?????try {
?????????// access the resource protected by this lock
?????} finally {
?????????l.unlock();
?????}
?
三、線程的狀態(tài)
1.線程的六種狀態(tài)
(1)新建狀態(tài)(new)
線程剛被創(chuàng)建,但是并未啟動。還沒調(diào)用start方法。
(2)可運行狀態(tài)(Runnable)
線程可以在java虛擬機中運行的狀態(tài),可能正在運行自己代碼,也可能沒有,這取決于操作系統(tǒng)處理器。
(3)受阻塞狀態(tài)(Blocked)
當(dāng)一個線程試圖獲取一個對象鎖,而該對象鎖被其他的線程持有,則該線程進入Blocked狀態(tài);當(dāng)該線程持有鎖時,該線程將變成Runnable狀態(tài)。
(4)限時等待狀態(tài)(TimeWaiting)
同waiting狀態(tài),有幾個方法有超時參數(shù),調(diào)用他們將進入Timed Waiting狀態(tài)。這一狀態(tài)將一直保持到超時期滿或者接收到喚醒通知。帶有超時參數(shù)的常用方法有Thread.sleep()、鎖對象.wait()。
(5)無限等待狀態(tài)(Waiting)
一個線程在等待另一個線程執(zhí)行一個(喚醒)動作時,該線程進入Waiting狀態(tài)。進入這個狀態(tài)后是不能自動喚醒的,必須等待另一個線程調(diào)用notify或者notifyAll方法才能夠喚醒。
(6)消亡狀態(tài)(Teminated)
因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了run方法而死亡。
四、代碼練習(xí)
1.賣包子案例
包子鋪賣包子,有包子時則不生產(chǎn),買家在沒包子時不消費包子,有包子時消費包子。
public class BaoZi {boolean flag=false;//判斷包子有沒有 } public class BaoZiTest {public static void main(String[] args) {BaoZi bz = new BaoZi();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {while (true) {//如果包子沒有了,調(diào)用wait()方法進入waiting狀態(tài)synchronized (bz) {if (bz.flag == false) {try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("吃貨醒來了");System.out.println("吃貨吃包子");System.out.println("吃貨吃完了...");//包子吃完了,使flag=false并喚醒做包子線程bz.flag = false;bz.notify();}}}});t1.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {while (true) {synchronized (bz) {//如果還有包子,調(diào)用wait()方法進入waiting狀態(tài)if (bz.flag == true) {try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("包子鋪發(fā)現(xiàn)沒包子了");System.out.println("包子鋪做包子");System.out.println("包子已經(jīng)上架了...");//包子做出來了,使flag=true并喚醒吃包子線程bz.flag = true;bz.notify();}}}});t2.start();System.out.println("hello");} }2.過山洞案例
請按要求編寫多線程應(yīng)用程序,模擬多個人通過一個山洞: 1.這個山洞每次只能通過一個人,每個人通過山洞的時間為5秒; 2.隨機生成10個人,同時準(zhǔn)備過此山洞,并且定義一個變量用于記錄通過隧道的人數(shù)。 顯示每次通過山洞人的姓名,和通過順序; import java.util.ArrayList; import java.util.Collections; import java.util.Random;public class GuoShanDong {ArrayList<String> names = new ArrayList<>();int a=1;public GuoShanDong() {Collections.addAll(names, "路飛", "索隆", "娜美", "喬巴", "香吉士","烏索普", "弗蘭奇", "甚平", "布魯克", "羅賓");}public synchronized void guoShanDong() {Random r = new Random();if (names.size() > 0) {int i = r.nextInt(names.size());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println((a++)+"---"+Thread.currentThread().getName() + "---" + names.remove(i) + "正在通過山洞...");}} } public class Test09 {static int a = 1;public static void main(String[] args) {GuoShanDong gsd = new GuoShanDong();int i = 0;new Thread(new Runnable() {@Overridepublic void run() {while (gsd.a <= 10) {gsd.guoShanDong();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {while (gsd.a <= 10) {gsd.guoShanDong();}}}).start();} }?
總結(jié)