java多线程总结(二)
線程一般有6個狀態(tài):
新建狀態(tài):NEW
可運行狀態(tài):RUNNABLE
休眠狀態(tài):TIMED_WAITING
等待狀態(tài):WAITING
阻塞狀態(tài):BLOCKED
終止狀態(tài)“TERMINATED
當我們使用new創(chuàng)建線程之后,線程處于新建狀態(tài),當調(diào)用start方法之后,線程出于可運行狀態(tài),當線程需要獲得對象的內(nèi)置鎖,而這個鎖被其他線程所占用的時候,線程就出于阻塞狀態(tài),當線程等待其他線程通知調(diào)度表可以運行時,線程處于等待狀態(tài),當一個含有時間參數(shù)的方法,必須sleep()方法,可以讓線程處于計時等待狀態(tài),當run()方法運行完畢或者出現(xiàn)異常,線程處于終止狀態(tài)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package?Thread; public?class?ThreadStateDemo{ ????public?static?void?main(String[] args)?throws?Exception{ ????????ThreadState state =?new?ThreadState(); ????????Thread demo =?new?Thread(state); ????????System.out.println("新建狀態(tài):"?+ demo.getState()); ????????demo.start(); ????????System.out.println("可運行狀態(tài):"?+ demo.getState()); ????????Thread.sleep(100); ????????System.out.println("休眠狀態(tài):"?+ demo.getState()); ????????Thread.sleep(1000); ????????System.out.println("等待狀態(tài):"?+ demo.getState()); ????????state.notifyWait(); ????????System.out.println("阻塞狀態(tài):"?+ demo.getState()); ????????Thread.sleep(1000); ????????System.out.println("終止狀態(tài)“"?+ demo.getState()); ????} } class?ThreadState?implements?Runnable{ ????@Override ????public?void?run(){ ????????try{ ????????????waitForASecond(); ????????????waitForAYear(); ????????}catch(Exception e){ ????????????e.printStackTrace(); ????????} ????} ????// 當前線程等待1秒 ????public?synchronized?void?waitForASecond()?throws?Exception{ ????????wait(1000); ????} ????// 當前線程一直等待 ????public?synchronized?void?waitForAYear()?throws?Exception{ ????????wait(); ????} ????// 喚醒線程 ????public?synchronized?void?notifyWait()?throws?Exception{ ????????notify(); ????} } |
?
【運行結(jié)果】:
?
新建狀態(tài):NEW
?
可運行狀態(tài):RUNNABLE
?
休眠狀態(tài):TIMED_WAITING
?
等待狀態(tài):WAITING
?
阻塞狀態(tài):BLOCKED
?
終止狀態(tài)“TERMINATED
?
?
?
線程組表示一個線程線程的集合,線程組中也可以包含其他的線程組。線程組構(gòu)成一棵樹。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | package?Thread; import?java.util.ArrayList; import?java.util.List; public?class?ThreadGroupDemo{ ????public?static?void?main(String[] args){ ????????for(String str : getThreadGroups(GetRootThreadGroups())){ ????????????System.out.println(str); ????????} ????} ????// 獲得根線程組 ????private?static?ThreadGroup GetRootThreadGroups(){ ????????// 獲得當前的線程組 ????????ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); ????????while(true){ ????????????if(rootGroup.getParent() !=?null){ ????????????????rootGroup = rootGroup.getParent(); ????????????}else{ ????????????????break; ????????????} ????????} ????????return?rootGroup; ????} ????// 獲得給定線程組中所有線程名 ????public?static?List<String> getThreads(ThreadGroup group){ ????????List<String> threadList =?new?ArrayList<String>(); ????????Thread[] threads =?new?Thread[group.activeCount()]; ????????int?count = group.enumerate(threads,?false); ????????for(int?i =?0; i < count; i++){ ????????????threadList.add(group.getName() +?" 線程組 "?+ threads[i].getName()); ????????} ????????return?threadList; ????} ????// 獲得線程組中所有子線程組 ????public?static?List<String> getThreadGroups(ThreadGroup group){ ????????List<String> threadList = getThreads(group); ????????ThreadGroup[] groups =?new?ThreadGroup[group.activeGroupCount()]; ????????int?count = group.enumerate(groups,?false); ????????for(int?i =?0; i < count; i++){ ????????????threadList.addAll(getThreads(groups[i])); ????????} ????????return?threadList; ????} } |
?
【運行結(jié)果】:
?
system?線程組?Reference Handler
?
system?線程組?Finalizer
?
system?線程組?Signal Dispatcher
?
system?線程組?Attach Listener
?
main?線程組?main
?
?
?
使用守護線程
?
java中的線程分為2類,用戶線程和守護線程,守護線程主要為其他線程提供服務(wù),守護線程會隨時被中斷,所以一般不要再守護線程中使用需要釋放資源的資源,比如輸入輸出流等,守護線程一般都是后臺線程,如果虛擬機只剩下守護線程,虛擬機就會退出。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package?Thread; public?class?DaemonThreadTest{ ????public?static?void?main(String[] args){ ????????Thread worker =?new?Thread(new?Worker()); ????????Thread timer =?new?Thread(new?Timer()); ????????//設(shè)置守護線程 ????????timer.setDaemon(true); ????????worker.start(); ????????timer.start(); ????} } class?Worker?implements?Runnable{ ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?5; ++i){ ????????????System.out.println("rollen真帥! 第"?+ i +?"次"); ????????} ????} } class?Timer?implements?Runnable{ ????@Override ????public?void?run(){ ????????long?currentTime = System.currentTimeMillis(); ????????long?processTime =?0; ????????while(true){ ????????????if((System.currentTimeMillis() - currentTime) > processTime){ ????????????????processTime = System.currentTimeMillis() - currentTime; ????????????????System.out.println("程序運行時間:"?+ processTime); ????????????} ????????} ????} } |
?
rollen真帥!?第0次
?
程序運行時間:1
?
程序運行時間:2
?
程序運行時間:3
?
程序運行時間:4
?
程序運行時間:5
?
rollen真帥!?第1次
?
rollen真帥!?第2次
?
rollen真帥!?第3次
?
rollen真帥!?第4次
?
程序運行時間:6
?
終止指定的線程
?
雖然在Thread類中提供了stop()方法可以終止線程,但是由于其固有的不安全性,所以一般不要采用,本例子只是起到拋磚引玉的作用。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package?Thread; import?java.awt.FlowLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; public?class?ThreadStopDemo?extends?JFrame{ ????public?ThreadStopDemo(){ ????????panel.setLayout(new?FlowLayout(FlowLayout.CENTER)); ????????panel.add(label); ????????panel.add(startButton); ????????panel.add(endButton); ????????setContentPane(panel); ????????setSize(200,?300); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????????startButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????counter =?new?CounterThread(); ????????????????new?Thread(counter).start(); ????????????} ????????}); ????????endButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????if(counter ==?null){ ????????????????????return; ????????????????} ????????????????counter.setStop(false); ????????????} ????????}); ????} ????public?static?void?main(String[] args){ ????????new?ThreadStopDemo(); ????} ????class?CounterThread?implements?Runnable{ ????????@Override ????????public?void?run(){ ????????????while(this.flag){ ????????????????try{ ????????????????????Thread.sleep(500); ????????????????}catch(Exception e){ ????????????????????e.printStackTrace(); ????????????????} ????????????????label.setText("更新"?+ (count++) +?"更新"); ????????????} ????????} ????????public?void?setStop(boolean?flag){ ????????????this.flag = flag; ????????} ????????private?int?count =?0; ????????private?boolean?flag =?true; ????} ????private?CounterThread counter =?null; ????private?final?JPanel panel =?new?JPanel(); ????private?final?JLabel label =?new?JLabel("更新0次"); ????private?final?JButton startButton =?new?JButton("開始"); ????private?final?JButton endButton =?new?JButton("結(jié)束"); } |
?
?
【運行結(jié)果】:
?
?
?
?
?
線程的插隊
?
在編寫多線程的程序的時候,經(jīng)常會遇到讓一個線程優(yōu)先于另外i個線程運行的情況,此時,除了設(shè)置這個線程的優(yōu)先級高(不推薦這種方法)之外,更加直接的辦法是采用Thread類中的join()方法。當插隊的線程運行結(jié)束之后,其他的線程才能運行。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package?Thread; public?class?ThreadJoinDemo{ ????public?static?void?main(String[] args){ ????????Thread demo1 =?new?Thread(new?EmergencyThread()); ????????demo1.start(); ????????for(int?i =?0; i <?5; ++i){ ????????????try{ ????????????????Thread.sleep(100); ????????????}catch(InterruptedException e){ ????????????????e.printStackTrace(); ????????????} ????????????System.out.println("正常情況 :"?+ i +?"號車開始出發(fā)"); ????????????try{ ????????????????//開始插隊 ????????????????demo1.join(); ????????????}catch(InterruptedException e){ ????????????????e.printStackTrace(); ????????????} ????????} ????} } class?EmergencyThread?implements?Runnable{ ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?5; ++i){ ????????????try{ ????????????????Thread.sleep(100); ????????????}catch(InterruptedException e){ ????????????????e.printStackTrace(); ????????????} ????????????System.out.println("緊急情況 :"?+ i +?"號車開始出發(fā)"); ????????} ????} } |
?
?
【運行結(jié)果】:
?
正常情況?:0號車開始出發(fā)
?
緊急情況:0號車開始出發(fā)
?
緊急情況:1號車開始出發(fā)
?
緊急情況:2號車開始出發(fā)
?
緊急情況:3號車開始出發(fā)
?
緊急情況:4號車開始出發(fā)
?
正常情況?:1號車開始出發(fā)
?
正常情況?:2號車開始出發(fā)
?
正常情況?:3號車開始出發(fā)
?
正常情況?:4號車開始出發(fā)
?
如果我們?nèi)サ鬸oin哪一行的話,運行結(jié)果:(結(jié)果不唯一)
?
緊急情況:0號車開始出發(fā)
?
正常情況?:0號車開始出發(fā)
?
緊急情況:1號車開始出發(fā)
?
正常情況?:1號車開始出發(fā)
?
正常情況?:2號車開始出發(fā)
?
緊急情況:2號車開始出發(fā)
?
緊急情況:3號車開始出發(fā)
?
正常情況?:3號車開始出發(fā)
?
正常情況?:4號車開始出發(fā)
?
緊急情況:4號車開始出發(fā)
?
線程的同步
?
多線程編程的一個重要原因是實現(xiàn)數(shù)據(jù)的共享,但是如果兩個線程同時修改一個數(shù)據(jù)的話,則會產(chǎn)生同步問題。
?
下面采用一個2個人同時往銀行存錢的例子,銀行卡初始金額為100元。每次存10元,大家仔細查看余額。(對于簡單的多線程,出錯的概率很小,今天很不巧,我一向地下的RP今天居然爆發(fā)了,實驗了很多次,都沒錯,最后終于出現(xiàn)了)
?
先看一下結(jié)果吧:
?
?
?
案例說兩側(cè)不能出現(xiàn)一樣余額的。
?
代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | package?Thread; import?java.awt.GridLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; import?javax.swing.JScrollPane; import?javax.swing.JTextArea; public?class?UnSynBank?extends?JFrame{ ????public?UnSynBank(){ ????????panel.setLayout(new?GridLayout(2,?2,?3,?3)); ????????panel.add(label1); ????????panel.add(label2); ????????JScrollPane js1 =?new?JScrollPane(oneArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js1); ????????JScrollPane js2 =?new?JScrollPane(twoArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js2); ????????panel2.add(panel); ????????panel2.add(statrButton); ????????setContentPane(panel2); ????????statrButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????Thread demo1 =?new?Thread(new?Transfer(bank, oneArea)); ????????????????demo1.start(); ????????????????Thread demo2 =?new?Thread(new?Transfer(bank, twoArea)); ????????????????demo2.start(); ????????????} ????????}); ????????setSize(300,?400); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????} ????public?static?void?main(String[] args){ ????????new?UnSynBank(); ????} ????private?final?Bank bank =?new?Bank(); ????JPanel panel =?new?JPanel(); ????JPanel panel2 =?new?JPanel(); ????private?final?JButton statrButton =?new?JButton("開始存錢"); ????private?final?JLabel label1 =?new?JLabel("一號線程"); ????private?final?JLabel label2 =?new?JLabel("二號線程"); ????private?final?JTextArea oneArea =?new?JTextArea(5,?10); ????private?final?JTextArea twoArea =?new?JTextArea(5,?10); } /** ?* 表示銀行的類 ?* */ class?Bank{ ????public?Bank(){ ????} ????public?void?deposit(int?money){ ????????account += money; ????} ????public?int?getAccount(){ ????????return?account; ????} ????private?int?account =?100; } /** ?* 表示往賬戶存錢 ?* */ class?Transfer?implements?Runnable{ ????public?Transfer(){ ????} ????public?Transfer(Bank bank, JTextArea textArea){ ????????this.bank = bank; ????????this.textArea = textArea; ????} ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?20; ++i){ ????????????bank.deposit(10); ????????????String str = textArea.getText(); ????????????textArea.setText(str +?"賬戶余額為:"?+ bank.getAccount() +?"\n"); ????????} ????} ????private?Bank bank =?null; ????private?JTextArea textArea =?null; } |
?
?
?
因為一個進程中的所有線程會共享進程中的資源,所以當一個線程還沒有將修改之后的結(jié)果保存的時候,另外一個線程卻進行讀取,這樣自然會產(chǎn)生錯誤,所以這個時候就需要我們采用同步來解決問題的。
?
使用同步方法實現(xiàn)同步
?
所謂同步方法,就是用synchronized修飾的方法,之所以這十幾個字母能解決困難的同步問題,這是和java中的內(nèi)置鎖密切相關(guān)的,每一個java對象中有一個內(nèi)置鎖,如果方法使用了synchronized進行修飾的話,內(nèi)置鎖會保護整個方法,也就是在調(diào)用方法之前,需要、獲得內(nèi)置鎖,否則就會處于阻塞狀態(tài),當然這個關(guān)鍵字也可以修飾靜態(tài)方法,如果調(diào)用靜態(tài)方法,就會鎖住整個類
但是要提醒一下大家,同步是一種高開銷的操作,所以應(yīng)該盡量減少需要同步的內(nèi)容
?
使用特殊域變量實現(xiàn)同步
volatile提供了一種免鎖的機制,使用這個關(guān)鍵字修飾的域相當于告訴虛擬機,這個域可能會被其他的線程跟新,因此每次讀取這個域的時候都需要重新計算,而不是使用寄存器中的值,這個關(guān)鍵字不會提供任何的原子操作,也不能用來修飾final類型的變量、
?修改后代碼為:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | package?com.java; import?java.awt.GridLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; ?? import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; import?javax.swing.JScrollPane; import?javax.swing.JTextArea; ?? public?class?UnSynBank?extends?JFrame{ ????public?UnSynBank(){ ????????panel.setLayout(new?GridLayout(2,?2,?3,?3)); ????????panel.add(label1); ????????panel.add(label2); ????????JScrollPane js1 =?new?JScrollPane(oneArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js1); ????????JScrollPane js2 =?new?JScrollPane(twoArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js2); ?? ????????panel2.add(panel); ????????panel2.add(statrButton); ????????setContentPane(panel2); ?? ????????statrButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????Thread demo1 =?new?Thread(new?Transfer(bank, oneArea)); ????????????????demo1.start(); ????????????????Thread demo2 =?new?Thread(new?Transfer(bank, twoArea)); ????????????????demo2.start(); ????????????} ????????}); ?? ????????setSize(300,?400); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????} ?? ????public?static?void?main(String[] args){ ????????new?UnSynBank(); ????} ?? ????private?final?Bank bank =?new?Bank(); ????JPanel panel =?new?JPanel(); ????JPanel panel2 =?new?JPanel(); ????private?final?JButton statrButton =?new?JButton("開始存錢"); ????private?final?JLabel label1 =?new?JLabel("一號線程"); ????private?final?JLabel label2 =?new?JLabel("二號線程"); ????private?final?JTextArea oneArea =?new?JTextArea(5,?10); ????private?final?JTextArea twoArea =?new?JTextArea(5,?10); } ?? /** ?* 表示銀行的類 ?* */ class?Bank{ ????public?Bank(){ ?? ????} ?? ????public?synchronized?void?deposit(int?money){ ????????account += money; ????} ?? ????public?int?getAccount(){ ????????return?account; ????} ?? ????private?volatile?int?account =?100; } ?? /** ?* 表示往賬戶存錢 ?* */ class?Transfer?implements?Runnable{ ????public?Transfer(){ ?? ????} ?? ????public?Transfer(Bank bank, JTextArea textArea){ ????????this.bank = bank; ????????this.textArea = textArea; ????} ?? ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?20; ++i){ ????????????bank.deposit(10); ????????????String str = textArea.getText(); ????????????textArea.setText(str +?"賬戶余額為:"?+ bank.getAccount() +?"\n"); ????????} ????} ?? ????private?Bank bank =?null; ????private?JTextArea textArea =?null; } |
提醒一下:關(guān)于安全域的并發(fā)訪問:
?
多線程中的非同步問題出現(xiàn)在對于域的讀寫上的時候,如果讓域自身避免這個問題的話,則不需要修改操作的方法,在java中有3中域自身就可以避免非同步問題:final域,使用volatile域,以及有鎖保護的域。
?
?
使用重入鎖實現(xiàn)線程同步
| 1 2 | import?java.util.concurrent.locks.Lock; import?java.util.concurrent.locks.ReentrantLock; |
?
可以在程序中添加上面兩行代碼,。然后將Bank修改為:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class?Bank{ ????public?Bank(){ ????} ????public?void?deposit(int?money){ ????????lock.lock(); ????????try{ ????????????account += money; ????????}finally{ ????????????lock.unlock(); ????????} ????} ????public?int?getAccount(){ ????????return?account; ????} ????private?final?Lock lock =?new?ReentrantLock(); ????private?int?account =?100; } |
?
這樣也可以解決非同步問題。至于這個類,大家可以自行去查看API,我只是在這里提醒一下,如果synchronized能夠滿足需求的話,就使用synchronized關(guān)鍵字,因為這個可以簡化代碼,如果需要更加高級的功能的時候,就使用Lock對象,在使用ReentrantLock的時候,一定要注意及時釋放鎖,否則程序會出現(xiàn)死鎖。
?
使用線程局部變量實現(xiàn)線程同步
?
這個例子演示的是兩個線程同時修改一個變量,運行結(jié)果:
?
?
可以發(fā)現(xiàn),每個線程完成修改之后的副本是完全獨立的,如果使用TreadLocal來管理變量,則每個使用這個變量的線程都會獲得這個變量的一個副本。,并且可以隨意修改這個副本,每個線程之間不會影響。
?
TreadLocal和同步機制都是為了解決多線程中的相同變量訪問沖突的問題的,前者采用的是空間還時間,后者采用的是時間換空間
?
代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class?Bank{ ????public?Bank(){ ????} ????public?void?deposit(int?money){ ????????account.set(account.get() + money); ????} ????public?int?getAccount(){ ????????return?account.get(); ????} ????private?static?ThreadLocal<Integer> account =?new?ThreadLocal<Integer>(){ ????????@Override ????????protected?Integer initialValue(){ ????????????return?100; ????????} ????}; } |
?
線程之間的通信
?
還記得我在我的筆記java IO總結(jié)中給出了一個使用管道流進行線程之間通信的例子:
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | package?Thread; import?java.io.IOException; import?java.io.PipedInputStream; import?java.io.PipedOutputStream; /** ?* 使用管道流進行線程之間的通信 ?* */ public?class?PipedTreadDemo{ ????public?static?void?main(String[] args){ ????????Sender send =?new?Sender(); ????????Reciver rec =?new?Reciver(); ????????try{ ????????????send.getOut().connect(rec.getReciver()); ????????}catch(Exception e){ ????????????e.printStackTrace(); ????????} ????????new?Thread(send).start(); ????????new?Thread(rec).start(); ????} } /** ?* 消息發(fā)送類 ?* */ class?Sender?implements?Runnable{ ????private?PipedOutputStream out =?null; ????public?Sender(){ ????????out =?new?PipedOutputStream(); ????} ????public?PipedOutputStream getOut(){ ????????return?this.out; ????} ????@Override ????public?void?run(){ ????????String str =?"rollen holt"; ????????try{ ????????????out.write(str.getBytes()); ????????}catch(IOException e){ ????????????e.printStackTrace(); ????????} ????????try{ ????????????out.close(); ????????}catch(IOException e){ ????????????e.printStackTrace(); ????????} ????} } /** ?* 消息接受類 ?* */ class?Reciver?implements?Runnable{ ????private?PipedInputStream input =?null; ????public?Reciver(){ ????????input =?new?PipedInputStream(); ????} ????public?PipedInputStream getReciver(){ ????????return?this.input; ????} ????@Override ????public?void?run(){ ????????byte[] bytes =?new?byte[1024]; ????????int?len =?0; ????????try{ ????????????len = input.read(bytes); ????????}catch(IOException e){ ????????????e.printStackTrace(); ????????} ????????try{ ????????????input.close(); ????????}catch(IOException e){ ????????????e.printStackTrace(); ????????} ????????System.out.println("讀取的內(nèi)容為:"?+?new?String(bytes,?0, len)); ????} } |
?
?
【運行結(jié)果】:
?
讀取的內(nèi)容為:rollen holt
?
下面,我們在同步的前提下,在舉出一個線程通信的例子:
?
首先擺出運行結(jié)果再說:
?
?
程序代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | package?Thread; /** ?* 線程之間的通信 ?* */ import?java.awt.GridLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; import?javax.swing.JScrollPane; import?javax.swing.JTextArea; public?class?TreadCommunicate?extends?JFrame{ ????public?TreadCommunicate(){ ????????panel.setLayout(new?GridLayout(2,?2,?3,?3)); ????????panel.add(label1); ????????panel.add(label2); ????????JScrollPane js1 =?new?JScrollPane(oneArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js1); ????????JScrollPane js2 =?new?JScrollPane(twoArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js2); ????????statrButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????Sender sender =?new?Sender(); ????????????????Thread demo1 =?new?Thread(sender); ????????????????Thread demo2 =?new?Thread(new?Receiver(sender)); ????????????????demo1.start(); ????????????????demo2.start(); ????????????} ????????}); ????????panel2.add(panel); ????????panel2.add(statrButton); ????????setContentPane(panel2); ????????setSize(300,?400); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????} ????public?static?void?main(String[] args){ ????????new?TreadCommunicate(); ????} ????/** ?????* 賣家 ?????* */ ????class?Sender?implements?Runnable{ ????????@Override ????????public?void?run(){ ????????????for(int?i =?0; i <?5; ++i){ ????????????????// 如果已經(jīng)發(fā)送,那么就等待 ????????????????while(isValid){ ????????????????????Thread.yield(); ????????????????} ????????????????product = products[i]; ????????????????String text = oneArea.getText(); ????????????????oneArea.setText(text +?"發(fā)送"?+ product +?"\n"); ????????????????try{ ????????????????????Thread.sleep(1000); ????????????????}catch(Exception e){ ????????????????????e.printStackTrace(); ????????????????} ????????????????isValid =?true; ????????????} ????????} ????????public?boolean?isisValid(){ ????????????return?this.isValid; ????????} ????????public?void?setValid(boolean?flag){ ????????????this.isValid = flag; ????????} ????????public?String getProduct(){ ????????????return?product; ????????} ????????private?volatile?String[] products = {?"《金 瓶梅》",?"《紅樓夢》",?"《平凡的世界》", ????????????????"《流氓老師》",?"《西游記》"?}; ????????private?volatile?boolean?isValid =?false; ????????private?volatile?String product; ????}// end sender ????/** ?????* 買家 ?????* */ ????class?Receiver?implements?Runnable{ ????????public?Receiver(){ ????????} ????????public?Receiver(Sender sender){ ????????????this.sender = sender; ????????} ????????@Override ????????public?void?run(){ ????????????for(int?i =?0; i <?5; ++i){ ????????????????// 如果沒有發(fā)送,就等待 ????????????????while(!sender.isisValid()){ ????????????????????Thread.yield(); ????????????????} ????????????????String test = twoArea.getText(); ????????????????twoArea.setText(test +?"接受到"?+ sender.getProduct() +?"\n"); ????????????????try{ ????????????????????Thread.sleep(1000); ????????????????}catch(Exception e){ ????????????????????e.printStackTrace(); ????????????????} ????????????????sender.setValid(false); ????????????} ????????} ????????private?Sender sender; ????} ????JPanel panel =?new?JPanel(); ????JPanel panel2 =?new?JPanel(); ????private?final?JButton statrButton =?new?JButton("開始交易"); ????private?final?JLabel label1 =?new?JLabel("賣家"); ????private?final?JLabel label2 =?new?JLabel("買家"); ????private?final?JTextArea oneArea =?new?JTextArea(5,?10); ????private?final?JTextArea twoArea =?new?JTextArea(5,?10); } |
?
死鎖的范例
?
下面絕對不是本人蛋疼的寫出這個一個更加叫人蛋疼的程序。只是給出了一個例子:
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /** ?* 簡單的死鎖 ?* */ public?class?DeadLockDemo?implements?Runnable{ ????@Override ????public?void?run(){ ????????// 獲得當前線程的名字 ????????String str = Thread.currentThread().getName(); ????????System.out.println(str +?": flag= "?+ flag); ????????if(flag){ ????????????synchronized?(obj1){ ????????????????try{ ????????????????????Thread.sleep(1000); ????????????????}catch(Exception e){ ????????????????????e.printStackTrace(); ????????????????} ????????????????System.out.println(str +?"已經(jīng)進入同步快obj1,準備進入同步快obj2"); ????????????????synchronized?(obj2){ ????????????????????System.out.println(str +?"已經(jīng)進入同步快obj2"); ????????????????} ????????????} ????????} ????????if(!flag){ ????????????synchronized?(obj2){ ????????????????try{ ????????????????????Thread.sleep(1000); ????????????????}catch(Exception e){ ????????????????????e.printStackTrace(); ????????????????} ????????????????System.out.println(str +?"已經(jīng)進入同步快obj2,準備進入同步快obj1"); ????????????????synchronized?(obj1){ ????????????????????System.out.println(str +?"已經(jīng)進入同步快obj1"); ????????????????} ????????????} ????????} ????} ????public?static?void?main(String[] args){ ????????DeadLockDemo demo1 =?new?DeadLockDemo(); ????????DeadLockDemo demo2 =?new?DeadLockDemo(); ????????demo1.flag =?true; ????????demo2.flag =?false; ????????new?Thread(demo1).start(); ????????new?Thread(demo2).start(); ????} ????private?boolean?flag; ????private?final?Object obj1 =?new?Object(); ????private?final?Object obj2 =?new?Object(); } |
?
?
【運行結(jié)果】
?
(我承認我今天RP爆發(fā),我運行了10次,還是沒出現(xiàn)死鎖那種情況,但是這個程序確實可以產(chǎn)生死鎖的,哪位運行這個程序,要是產(chǎn)生了死鎖,麻煩說一下,謝謝)
?
?
?
使用線程池優(yōu)化多線程編程
?
這個例子使用的是Executors類,讀者自行查看API,因為要解說的話,就太多了。
?
下面這個例子給出了使用線程池和不使用線程池的情況下的效率的問題。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package?Thread; import?java.util.concurrent.ExecutorService; import?java.util.concurrent.Executors; /** ?* 使用線程池優(yōu)化多線程編程 ?* */ public?class?ThreadPoolDemo{ ????public?static?void?main(String[] args){ ????????Runtime run = Runtime.getRuntime(); ????????// 為了減少誤差 ????????run.gc(); ????????long?currentTime = System.currentTimeMillis(); ????????long?freemonery = run.freeMemory(); ????????for(int?i =?0; i <?10000; ++i){ ????????????new?Thread(new?Temo()).start(); ????????} ????????System.out.println("獨立運行10000個線程占用內(nèi)存為:" ????????????????+ (freemonery - run.freeMemory())); ????????System.out.println("獨立運行10000個線程占用時間為:" ????????????????+ (System.currentTimeMillis() - currentTime)); ????????// 下面使用線程池來試試 ????????run.gc(); ????????freemonery = run.freeMemory(); ????????currentTime = System.currentTimeMillis(); ????????ExecutorService executorService = Executors.newFixedThreadPool(3); ????????for(int?i =?0; i <?10000; ++i){ ????????????executorService.submit(new?Temo()); ????????} ????????System.out.println("使用線程池運行10000個線程占用內(nèi)存為:" ????????????????+ (freemonery - run.freeMemory())); ????????System.out.println("使用線程池運行10000個線程占用時間為:" ????????????????+ (System.currentTimeMillis() - currentTime)); ????} } class?Temo?implements?Runnable{ ????@Override ????public?void?run(){ ????????count++; ????} ????private?int?count =?0; } |
?
?
【運行結(jié)果】:
?
獨立運行10000個線程占用內(nèi)存為:3490440
?
獨立運行10000個線程占用時間為:1808
?
使用線程池運行10000個線程占用內(nèi)存為:1237424
?
使用線程池運行10000個線程占用時間為:62
?
關(guān)于哲學(xué)家就餐的問題
?
由于代碼比較長,所以單獨列出為一篇文章
?
地址:http://www.cnblogs.com/rollenholt/archive/2011/09/15/2178004.html
?
使用信號量實現(xiàn)線程同步
?
現(xiàn)在我們繼續(xù)回答之前銀行存款的問題,相信大家還沒有忘記,哈哈,真是不好意思,本來線程同步這一塊應(yīng)該整理在一起的。
?
一個信號量有3中操作,而且他們?nèi)慷际窃拥?#xff0c;初始化,增加,減少。增加可以為一個進程解除阻塞,減少可以為一個進程進入阻塞。
?
Semaphore類是一個技術(shù)信號量,從概念上信號量維持了一個許可集。
?
現(xiàn)在我們繼續(xù)看上面的銀行存款問題:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | package?Thread; import?java.awt.GridLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; import?java.util.concurrent.Semaphore; import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; import?javax.swing.JScrollPane; import?javax.swing.JTextArea; public?class?synDemo?extends?JFrame{ ????public?synDemo(){ ????????panel.setLayout(new?GridLayout(2,?2,?3,?3)); ????????panel.add(label1); ????????panel.add(label2); ????????JScrollPane js1 =?new?JScrollPane(oneArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js1); ????????JScrollPane js2 =?new?JScrollPane(twoArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js2); ????????panel2.add(panel); ????????panel2.add(statrButton); ????????setContentPane(panel2); ????????statrButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????Thread demo1 =?new?Thread(new?Transfer1(bank, oneArea, ????????????????????????semaphore)); ????????????????demo1.start(); ????????????????Thread demo2 =?new?Thread(new?Transfer1(bank, twoArea, ????????????????????????semaphore)); ????????????????demo2.start(); ????????????} ????????}); ????????setSize(300,?400); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????} ????public?static?void?main(String[] args){ ????????new?synDemo(); ????} ????Semaphore semaphore =?new?Semaphore(1,?true); ????private?final?Bank1 bank =?new?Bank1(); ????JPanel panel =?new?JPanel(); ????JPanel panel2 =?new?JPanel(); ????private?final?JButton statrButton =?new?JButton("開始存錢"); ????private?final?JLabel label1 =?new?JLabel("一號線程"); ????private?final?JLabel label2 =?new?JLabel("二號線程"); ????private?final?JTextArea oneArea =?new?JTextArea(5,?10); ????private?final?JTextArea twoArea =?new?JTextArea(5,?10); } /** ?* 表示銀行的類 ?* */ class?Bank1{ ????public?Bank1(){ ????} ????public?void?deposit(int?money){ ????????account += money; ????} ????public?int?getAccount(){ ????????return?account; ????} ????private?int?account; } /** ?* 表示往賬戶存錢 ?* */ class?Transfer1?implements?Runnable{ ????public?Transfer1(){ ????} ????public?Transfer1(Bank1 bank, JTextArea textArea, Semaphore semaphore){ ????????this.bank = bank; ????????this.textArea = textArea; ????????this.semaphore = semaphore; ????} ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?20; ++i){ ????????????// 獲得許可 ????????????try{ ????????????????semaphore.acquire(); ????????????}catch(InterruptedException e){ ????????????????e.printStackTrace(); ????????????} ????????????bank.deposit(10); ????????????String str = textArea.getText(); ????????????textArea.setText(str +?"賬戶余額為:"?+ bank.getAccount() +?"\n"); ????????????// 釋放許可 ????????????semaphore.release(); ????????} ????} ????// 注意 ????private?Semaphore semaphore; ????private?Bank1 bank =?null; ????private?JTextArea textArea =?null; } |
運行結(jié)果:
?
?
使用原子變量實現(xiàn)線程同步
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | package?Thread; import?java.awt.GridLayout; import?java.awt.event.ActionEvent; import?java.awt.event.ActionListener; import?java.util.concurrent.atomic.AtomicInteger; import?javax.swing.JButton; import?javax.swing.JFrame; import?javax.swing.JLabel; import?javax.swing.JPanel; import?javax.swing.JScrollPane; import?javax.swing.JTextArea; public?class?synDemo?extends?JFrame{ ????public?synDemo(){ ????????panel.setLayout(new?GridLayout(2,?2,?3,?3)); ????????panel.add(label1); ????????panel.add(label2); ????????JScrollPane js1 =?new?JScrollPane(oneArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js1); ????????JScrollPane js2 =?new?JScrollPane(twoArea, ????????????????JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ????????????????JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ????????panel.add(js2); ????????panel2.add(panel); ????????panel2.add(statrButton); ????????setContentPane(panel2); ????????statrButton.addActionListener(new?ActionListener(){ ????????????@Override ????????????public?void?actionPerformed(ActionEvent e){ ????????????????Thread demo1 =?new?Thread(new?Transfer1(bank, oneArea)); ????????????????demo1.start(); ????????????????Thread demo2 =?new?Thread(new?Transfer1(bank, twoArea)); ????????????????demo2.start(); ????????????} ????????}); ????????setSize(300,?400); ????????setVisible(true); ????????setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ????} ????public?static?void?main(String[] args){ ????????new?synDemo(); ????} ????private?final?Bank1 bank =?new?Bank1(); ????JPanel panel =?new?JPanel(); ????JPanel panel2 =?new?JPanel(); ????private?final?JButton statrButton =?new?JButton("開始存錢"); ????private?final?JLabel label1 =?new?JLabel("一號線程"); ????private?final?JLabel label2 =?new?JLabel("二號線程"); ????private?final?JTextArea oneArea =?new?JTextArea(5,?10); ????private?final?JTextArea twoArea =?new?JTextArea(5,?10); } /** ?* 表示銀行的類 ?* */ class?Bank1{ ????public?Bank1(){ ????} ????public?void?deposit(int?money){ ????????account.addAndGet(money); ????} ????public?int?getAccount(){ ????????return?account.get(); ????} ????//注意 ????private?final?AtomicInteger account =?new?AtomicInteger(100); } /** ?* 表示往賬戶存錢 ?* */ class?Transfer1?implements?Runnable{ ????public?Transfer1(){ ????} ????public?Transfer1(Bank1 bank, JTextArea textArea){ ????????this.bank = bank; ????????this.textArea = textArea; ????} ????@Override ????public?void?run(){ ????????for(int?i =?0; i <?20; ++i){ ????????????bank.deposit(10); ????????????String str = textArea.getText(); ????????????textArea.setText(str +?"賬戶余額為:"?+ bank.getAccount() +?"\n"); ????????} ????} ????private?Bank1 bank =?null; ????private?JTextArea textArea =?null; } |
【運行結(jié)果】:
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的java多线程总结(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WorkerMan 入门学习之(二)基础
- 下一篇: sass使用相关报错