java 锁旗标_Java多线程
Java多線程
1. 多線程存在的意義
多線程最大的作用就是能夠創(chuàng)建“靈活響應(yīng)”的桌面程序,而編寫多線程最大的困難就是不同線程之間共享資源的問題,要使這些資源不會同時被多個線程訪問。由于CPU的調(diào)度具有一定的隨機性,因此使用多線程千萬別較真。
2. 進程與線程的區(qū)別
-進程:運行時(runtime)的應(yīng)用程序,進程之間的內(nèi)存是獨立的,進程之間的通信需要使用socket
-線程:進程中并發(fā)執(zhí)行的代碼段,線程之間的內(nèi)存時共享的,每一個運行著的線程對應(yīng)一個stack,一個應(yīng)用程序(進程)至少有一個線程(主線程)
3. 線程的創(chuàng)建方式
3.1 方式一:繼承Thread類
1、覆蓋重寫run()方法,將需要同步執(zhí)行的代碼編寫到run()方法中。
2、創(chuàng)建子類對象的同時線程被創(chuàng)建
3、調(diào)用Thread.start()方法開啟線程
3.1.1 Thread.yield()方法
yield()是靜態(tài)方法,與對象無關(guān)。讓當(dāng)前線程放棄cpu的搶占權(quán),具有謙讓之意,但動作是瞬時的,放棄之后又會立即去搶占cpu。
案例1:多個線程交替打印當(dāng)前系統(tǒng)時間,觀察輸出情況
1 packagecom.yss.gyg;2
3 importjava.util.Date;4
5 /*
6 * 案例1:多個線程交替打印當(dāng)前系統(tǒng)時間,觀察輸出情況7 **/
8 public class PrintSysDateTime extendsThread {9
10 @Override11 public voidrun() {12 String name =Thread.currentThread().getName();13 for (int i = 0; i < 10; i++) {14 System.out.println(name + ":"+newDate());15 //輸出完成一次之后放棄當(dāng)前cpu的搶占權(quán)
16 Thread.yield();17 }18 }19 }
PrintSysDateTime
1 packagecom.yss.gyg.test;2
3 importcom.yss.gyg.PrintSysDateTime;4
5 importjava.util.Date;6
7 public classPrintSysDateTimeTest {8 public static voidmain(String[] args) {9 PrintSysDateTime t1 = newPrintSysDateTime();10 PrintSysDateTime t2 = newPrintSysDateTime();11 PrintSysDateTime t3 = newPrintSysDateTime();12 t1.start();13 t2.start();14 t3.start();15 }16 }
PrintSysDateTimeTest
3.1.2 Thread.join()方法
join()方法:當(dāng)前線程需等待指定的線程結(jié)束后才能繼續(xù)運行。
案例2:打麻將需要4個人,需4個人都到了之后才能開局
1 packagecom.yss.gyg;2
3 public class Player extendsThread{4 privateString name;5 private inttime;6
7 public Player(String name, inttime) {8 this.name =name;9 this.time =time;10 }11
12 @Override13 public voidrun() {14 System.out.println(name + ":出發(fā)了");15 try{16 Thread.sleep(time);17 } catch(InterruptedException e) {18 e.printStackTrace();19 }20 System.out.println(name + ":到了");21 }22 }
Player
1 packagecom.yss.gyg.test;2
3 importcom.yss.gyg.Player;4
5 public classPlayerTest {6 public static voidmain(String[] args) {7 Player player1 = new Player("player1", 1000);8 Player player2 = new Player("player2", 1500);9 Player player3 = new Player("player3", 1500);10 Player player4 = new Player("player4", 1700);11 player1.start();12 player2.start();13 player3.start();14 player4.start();15 try{16 player1.join();17 player2.join();18 player3.join();19 player4.join();20 } catch(InterruptedException e) {21 e.printStackTrace();22 }23 System.out.println("開搞!!!");24 }25 }26 /*
27 player1:出發(fā)了28 player4:出發(fā)了29 player3:出發(fā)了30 player2:出發(fā)了31 player1:到了32 player2:到了33 player3:到了34 player4:到了35 開搞!!!36 **/
PlayerTest
3.1.3 t1.start()和直接調(diào)用run()方法的區(qū)別
直接調(diào)用run()方法:直接調(diào)用run方法就是普通方法的調(diào)用,run()方法的執(zhí)行依然是壓棧到mian()的棧中,每一個運行著的線程對應(yīng)一個stack
t1.start():我們只是告訴cpu可以調(diào)用run()方法了,具體又cpu開啟一個新的線程去調(diào)用run()方法,也就是另外開啟一個stack
3.1.4? Thread.setDaemon():設(shè)置守護線程
守護線程是為其它的線程服務(wù)的,當(dāng)一個進程中除了守護線程外的其它線程都執(zhí)行完時,則該進程結(jié)束。
案例3:Waiter為酒吧里的每一個包廂服務(wù),每一個包廂為一個線程,當(dāng)所有的包廂都結(jié)束之后Waiter下班,整個進程結(jié)束。
1 packagecom.yss.gyg;2
3 importjava.util.Date;4
5 public class Waiter extendsThread {6 @Override7 public voidrun() {8 //每隔2s鐘報一次時間
9 for (; ; ) { //死循環(huán)
10 System.out.println("當(dāng)前時間:" + newDate());11 try{12 Thread.sleep(2000);13 } catch(InterruptedException e) {14 e.printStackTrace();15 }16 }17 }18 }
Waiter
1 packagecom.yss.gyg;2
3 public class Box extendsThread{4 privateString name;5 private inttime;6
7 public Box(String name, inttime) {8 this.name =name;9 this.time =time;10 }11
12 @Override13 public voidrun() {14 System.out.println(name+":開始消費");15 try{16 Thread.sleep(time);17 } catch(InterruptedException e) {18 e.printStackTrace();19 }20 System.out.println(name+":消費結(jié)束");21
22 }23 }
Box
1 packagecom.yss.gyg.test;2
3 importcom.yss.gyg.Box;4 importcom.yss.gyg.Waiter;5
6 public classWaiterTest {7 public static voidmain(String[] args) {8 Waiter waiter = newWaiter();9 Box box1 = new Box("box1", 5000);10 Box box2 = new Box("box2", 8000);11 Box box3 = new Box("box3", 10000);12 Box box4 = new Box("box4", 7800);13 waiter.setDaemon(true);14 waiter.start();15 box1.start();16 box2.start();17 box3.start();18 box4.start();19 }20 }21 /*
22 box1:開始消費23 box4:開始消費24 box3:開始消費25 box2:開始消費26 當(dāng)前時間:Fri Dec 18 16:41:15 CST 202027 當(dāng)前時間:Fri Dec 18 16:41:17 CST 202028 當(dāng)前時間:Fri Dec 18 16:41:19 CST 202029 box1:消費結(jié)束30 當(dāng)前時間:Fri Dec 18 16:41:21 CST 202031 box4:消費結(jié)束32 box2:消費結(jié)束33 當(dāng)前時間:Fri Dec 18 16:41:23 CST 202034 box3:消費結(jié)束35 **/
WaiterTest
3.1.5 線程間資源共享(synchronize)
多線程之間的內(nèi)存是共享的,但多個線程同時訪問同一個資源的時候會出現(xiàn)線程安全問題,同步可以解決線程安全問題。
同步的前提:同步需要兩個或者兩個以上的線程,多個線程使用的是同一個鎖。
同步的方式:
同步代碼塊:同一時刻只能有一個線程執(zhí)行同步代碼塊中的代碼,同步代碼塊以指定的那個對象為“鎖旗標(biāo)”。同步代碼塊執(zhí)行期間,線程始終持有對象的監(jiān)控權(quán),其它的線程處于阻塞狀態(tài)。
1 synchronize(對象){
3 需要同步的代碼塊
5 }
同步非靜態(tài)方法:synchronize(this)=== 同步方法,以當(dāng)前對象為“鎖旗標(biāo)”
同步靜態(tài)方法:使用類的描述符(.class)作為“鎖旗標(biāo)”。
同步的弊端:當(dāng)線程比較多的時候,由于每個線程都要去判斷鎖的狀態(tài),需要消耗比較多的資源,因此會降低程序的運行效率。
案例4:多線程賣票:多個售票員同時賣一個票池中的票,直到票被全部賣完后結(jié)束進程。
1 packagecom.yss.gyg;2
3 public classTicketPoll {4 private int initNum;//初始化票池
5
6 public TicketPoll(intinitNum) {7 this.initNum =initNum;8 }9
10 //同步方法:以當(dāng)前對象為鎖旗標(biāo)
11 public synchronized int getTicket() {//取票的方法同時只能有一個線程執(zhí)行12 //有票返回票號
13 if (initNum > 0) {14 //initNum--;//原子性操作
15 return initNum--;16 }17 //沒票返回-1
18 return -1;19 }20 }
TicketPoll
1 packagecom.yss.gyg;2 /*售票員類*/
3 public class Seller extendsThread {4 privateString name;5 privateTicketPoll poll;6
7 publicSeller(String name, TicketPoll poll) {8 this.name =name;9 this.poll =poll;10 }11
12 @Override13 public voidrun() {14 int ticketNum = -1;15 while ((ticketNum = poll.getTicket()) > 0) {16 System.out.println(name + "賣出了 " + ticketNum + " 號票");17 try{18 //模擬售票時間
19 Thread.sleep(50);20 } catch(InterruptedException e) {21 e.printStackTrace();22 }23 }24
25 }26 }
Seller
1 packagecom.yss.gyg.test;2
3 importcom.yss.gyg.Seller;4 importcom.yss.gyg.TicketPoll;5
6 public classSellerTest {7 public static voidmain(String[] args) {8 TicketPoll poll = new TicketPoll(100);9 Seller seller1 = new Seller("seller1", poll);10 Seller seller2 = new Seller("seller2", poll);11 Seller seller3 = new Seller("seller3", poll);12 System.out.println("售票開始...");13 seller1.start();14 seller2.start();15 seller3.start();16
17 }18 }
SellerTest
3.1.6 線程間通信(wait()、notify()、notifyall())
wait()方法:讓當(dāng)前線程進入等待隊列,釋放cpu的搶占權(quán),并且還釋放“鎖旗標(biāo)”的監(jiān)控權(quán),進入等待隊列后等待Object.notify()來通知可以“搶”cpu了。
notify()方法:通知等待隊列中的一個線程喚醒,若等待隊列中有多個等待線程,將隨機選擇一個。
notifyall()方法:通知等待隊列中的所有線程喚醒,可以解決“死鎖”的象限。
案例5:多線程生產(chǎn)者、消費者問題:
1 packagecom.yss.gyg;2
3 public class Producer extendsThread{4 privateString name;5 privateP_C_Poll poll;6
7 publicProducer(String name, P_C_Poll poll) {8 this.name =name;9 this.poll =poll;10 }11
12 @Override13 public voidrun() {14 int i = 1;15 while (true) {16 poll.add(i);17 i++;18 try{19 sleep(500);//模擬生產(chǎn)時間
20 } catch(InterruptedException e) {21 e.printStackTrace();22 }23 }24 }25 }
生產(chǎn)者
1 packagecom.yss.gyg;2
3 public class Consumer extendsThread{4 privateString name;5 privateP_C_Poll poll;6
7 publicConsumer(String name, P_C_Poll poll) {8 this.name =name;9 this.poll =poll;10 }11
12 @Override13 public voidrun() {14 while (true) {15 poll.remove();16 try{17 sleep(200);//模擬消費時間
18 } catch(InterruptedException e) {19 e.printStackTrace();20 }21 }22 }23 }
消費者
1 packagecom.yss.gyg.test;2
3 importcom.yss.gyg.Consumer;4 importcom.yss.gyg.P_C_Poll;5 importcom.yss.gyg.Producer;6
7 public classP_C_Test {8 public static voidmain(String[] args) {9 P_C_Poll poll = newP_C_Poll();10 //10個生產(chǎn)者
11 for (int i = 0; i < 10; i++) {12 new Producer(i + "", poll).start();13 }14
15 //5個消費者
16 for (int i = 0; i < 5; i++) {17 new Consumer(i + "", poll).start();18 }19
20 }21 }
測試類
1 packagecom.yss.gyg;2
3 importjava.util.ArrayList;4 importjava.util.List;5
6 public classP_C_Poll {7 private int max = 100;8 private List list = new ArrayList<>();9
10 public synchronized void add(intnum) {11 int i = 0;12 //容器已滿
13 while ((i = list.size()) >=max) {14 notify();//通知消費
15 try{16 wait();//進入等待
17 } catch(InterruptedException e) {18 e.printStackTrace();19 }20 }21 //容器沒滿
22 list.add(num);23 System.out.println("生產(chǎn)了:" +num);24 System.out.println("容器中數(shù)量:" +list.size());25 notify();//通知消費
26 }27
28 public synchronized intremove() {29 int i = 0;30 //容器為空
31 while (list.isEmpty() && (i = list.size()) == 0) {32 notify();//通知生產(chǎn)
33 try{34 wait();//進入等待隊列
35 } catch(InterruptedException e) {36 e.printStackTrace();37 }38 }39 //容器不為空
40 int num = list.remove(0);41 System.out.println("消費了:" +num);42 System.out.println("容器中數(shù)量:" +list.size());43 notify();//通知生產(chǎn)
44 returnnum;45 }46 }
容器類
wait()、notify()、notifyall()方法為什么定義在Object類中?
wait()、notify()、notifyall()方法都是在同步中使用,而同步必須要有一個“鎖”,“鎖”可以是任意對象,因此定義在Object中。
sleep()和wait()方法的區(qū)別:
sleep()方法釋放cpu的搶占權(quán),不釋放“鎖”的監(jiān)控權(quán)
wait()方法釋放cpu的搶占權(quán),同時釋放“鎖”的監(jiān)控權(quán)
3.2 方式二:
-------------------------------end--------------------------------------------------------
總結(jié)
以上是生活随笔為你收集整理的java 锁旗标_Java多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: java httpclient 重定向_
 - 下一篇: 联想台式电脑bios如何设置u盘启动