本文實例講述了java線程同步方法。分享給大家供大家參考,具體如下:
1. semaphore
1.1 二進制semaphore
semaphore算是比較高級點的線程同步工具了,在許多其他語言里也有類似的實現。semaphore有一個最大的好處就是在初始化時,可以顯式的控制并發數。其內部維護這一個c計數器,當計數器小于等于0時,是不允許其他線程訪問并發區域的,反之則可以,因此,若將并發數設置為1,則可以確保單一線程同步。下面的例子模擬多線程打印,每個線程提交打印申請,然后執行打印,最后宣布打印結束,代碼如下:
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
|
import java.util.concurrent.semaphore; public class program{ public static void main(string[] agrs){ printqueue p= new printqueue(); thread[] ths= new thread[ 10 ]; for ( int i= 0 ;i< 10 ;i++){ ths[i]= new thread( new job(p), "thread" +i); } for ( int i= 0 ;i< 10 ;i++){ ths[i].start(); } } } class printqueue{ private semaphore s; public printqueue(){ s= new semaphore( 1 ); //二進制信號量 } public void printjob(object document){ try { s.acquire(); long duration=( long )(math.random()* 100 ); system.out.printf( "線程名:%s 睡眠:%d" ,thread.currentthread().getname(),duration); thread.sleep(duration); } catch (interruptedexception e){ e.printstacktrace(); } finally { s.release(); } } } class job implements runnable{ private printqueue p; public job(printqueue p){ this .p=p; } @override public void run(){ system.out.printf( "%s:正在打印一個任務\n " ,thread.currentthread().getname()); this .p.printjob( new object()); system.out.printf( "%s:文件已打印完畢\n " ,thread.currentthread().getname()); } } |
執行結果如下:
thread0:正在打印一個任務
thread9:正在打印一個任務
thread8:正在打印一個任務
thread7:正在打印一個任務
thread6:正在打印一個任務
thread5:正在打印一個任務
thread4:正在打印一個任務
thread3:正在打印一個任務
thread2:正在打印一個任務
thread1:正在打印一個任務
線程名:thread0 睡眠:32 thread0:文件已打印完畢
線程名:thread9 睡眠:44 thread9:文件已打印完畢
線程名:thread8 睡眠:45 thread8:文件已打印完畢
線程名:thread7 睡眠:65 thread7:文件已打印完畢
線程名:thread6 睡眠:12 thread6:文件已打印完畢
線程名:thread5 睡眠:72 thread5:文件已打印完畢
線程名:thread4 睡眠:98 thread4:文件已打印完畢
線程名:thread3 睡眠:58 thread3:文件已打印完畢
線程名:thread2 睡眠:24 thread2:文件已打印完畢
線程名:thread1 睡眠:93 thread1:文件已打印完畢
可以看到,所有線程提交打印申請后,按照并發順序一次執行,沒有任何并發沖突,誰先獲得信號量,誰就先執行,其他剩余線程均等待。這里面還有一個公平信號與非公平信號之說:基本上java所有的多線程工具都支持初始化的時候指定一個布爾變量,true時表明公平,即所有處于等待的線程被篩選的條件為“誰等的時間長就選誰進行執行”,有點first in first out的感覺,而false時則表明不公平(默認是不non-fairness),即所有處于等待的線程被篩選執行是隨機的。這也就是為什么多線程往往執行順序比較混亂的原因。
1.2 多重并發控制
若將上面的代碼改為s=new semaphore(3);//即讓其每次可以并發3條線程
,則輸出如下:
thread0:正在打印一個任務
thread9:正在打印一個任務
thread8:正在打印一個任務
thread7:正在打印一個任務
thread6:正在打印一個任務
thread5:正在打印一個任務
thread3:正在打印一個任務
thread4:正在打印一個任務
thread2:正在打印一個任務
thread1:正在打印一個任務
線程名:thread9 睡眠:26線程名:thread8 睡眠:46線程名:thread0 睡眠:79 thread9:文件已打印完畢
線程名:thread7 睡眠:35 thread8:文件已打印完畢
線程名:thread6 睡眠:90 thread7:文件已打印完畢
線程名:thread5 睡眠:40 thread0:文件已打印完畢
線程名:thread3 睡眠:84 thread5:文件已打印完畢
線程名:thread4 睡眠:13 thread4:文件已打印完畢
線程名:thread2 睡眠:77 thread6:文件已打印完畢
線程名:thread1 睡眠:12 thread1:文件已打印完畢
thread3:文件已打印完畢
thread2:文件已打印完畢
很明顯已經并發沖突了。若要實現分組(每組3個)并發嗎,則每一組也要進行同步,代碼修改如下:
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
|
import java.util.concurrent.semaphore; import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; public class program{ public static void main(string[] agrs){ printqueue p= new printqueue(); thread[] ths= new thread[ 10 ]; for ( int i= 0 ;i< 10 ;i++){ ths[i]= new thread( new job(p), "thread" +i); } for ( int i= 0 ;i< 10 ;i++){ ths[i].start(); } } } class printqueue{ private semaphore s; private boolean [] freeprinters; private lock lock; public printqueue(){ s= new semaphore( 3 ); //二進制信號量 freeprinters= new boolean [ 3 ]; for ( int i= 0 ;i< 3 ;i++){ freeprinters[i]= true ; } lock= new reentrantlock(); } public void printjob(object document){ try { s.acquire(); int printerindex=getindex(); long duration=( long )(math.random()* 100 ); system.out.printf( "線程名:%s 睡眠:%d\n" ,thread.currentthread().getname(),duration); thread.sleep(duration); freeprinters[printerindex]= true ; //恢復信號,供下次使用 } catch (interruptedexception e){ e.printstacktrace(); } finally { s.release(); } } //返回一個內部分組后的同步索引 public int getindex(){ int index=- 1 ; try { lock.lock(); for ( int i= 0 ;i<freeprinters.length;i++){ if (freeprinters[i]){ freeprinters[i]= false ; index=i; break ; } } } catch (exception e){ e.printstacktrace(); } finally { lock.unlock(); } return index; } } class job implements runnable{ private printqueue p; public job(printqueue p){ this .p=p; } @override public void run(){ system.out.printf( "%s:正在打印一個任務\n " ,thread.currentthread().getname()); this .p.printjob( new object()); system.out.printf( " %s:文件已打印完畢\n " ,thread.currentthread().getname()); } } |
其中getindex()
方法主要為了維護內部分組后(支持并發3個)組內數據的同步(用lock來同步)。
輸出如下:
thread0:正在打印一個任務
thread9:正在打印一個任務
thread8:正在打印一個任務
thread7:正在打印一個任務
thread6:正在打印一個任務
thread5:正在打印一個任務
thread4:正在打印一個任務
thread3:正在打印一個任務
thread2:正在打印一個任務
thread1:正在打印一個任務
線程名:thread0 睡眠:82 打印機:0號
線程名:thread8 睡眠:61 打印機:2號
線程名:thread9 睡眠:19 打印機:1號
thread9:文件已打印完畢
線程名:thread7 睡眠:82 打印機:1號
thread8:文件已打印完畢
線程名:thread6 睡眠:26 打印機:2號
thread0:文件已打印完畢
線程名:thread5 睡眠:31 打印機:0號
thread6:文件已打印完畢
線程名:thread4 睡眠:44 打印機:2號
thread7:文件已打印完畢
線程名:thread3 睡眠:54 打印機:1號
thread5:文件已打印完畢
線程名:thread2 睡眠:48 打印機:0號
thread4:文件已打印完畢
線程名:thread1 睡眠:34 打印機:2號
thread3:文件已打印完畢
thread2:文件已打印完畢
thread1:文件已打印完畢
2. countdownlatch
countdownlatch同樣也是支持多任務并發的一個工具。它主要用于“等待多個并發事件”,它內部也有一個計數器,當調用await()
方法時,線程處于等待狀態,只有當內部計數器為0時才繼續(countdown()
方法來減少計數),也就說,假若有一個需求是這樣的:主線程等待所有子線程都到達某一條件時才執行,那么只需要主線程await,然后在啟動每個子線程的時候進行countdown操作。下面模擬了一個開會的例子,只有當所有人員都到齊了,會議才能開始。
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
|
import java.util.concurrent.countdownlatch; public class program{ public static void main(string[] agrs){ //開啟可容納10人的會議室 videoconference v= new videoconference( 10 ); new thread(v).start(); //參與人員陸續進場 for ( int i= 0 ;i< 10 ;i++){ participant p= new participant(i+ "號人員" ,v); new thread(p).start(); } } } class videoconference implements runnable{ private countdownlatch controller; public videoconference( int num){ controller= new countdownlatch(num); } public void arrive(string name){ system.out.printf( "%s 已經到達!\n" ,name); controller.countdown(); system.out.printf( "還需要等 %d 個成員!\n" ,controller.getcount()); } @override public void run(){ try { system.out.printf( "會議正在初始化...!\n" ); controller.await(); system.out.printf( "所有人都到齊了,開會吧!\n" ); } catch (interruptedexception e){ e.printstacktrace(); } } } class participant implements runnable{ private videoconference conference; private string name; public participant(string name,videoconference conference){ this .name=name; this .conference=conference; } @override public void run(){ long duration=( long )(math.random()* 100 ); try { thread.sleep(duration); conference.arrive( this .name); } catch (interruptedexception e){ } } } |
輸出:
會議正在初始化...!
0號人員 已經到達!
還需要等 9 個成員!
1號人員 已經到達!
還需要等 8 個成員!
9號人員 已經到達!
還需要等 7 個成員!
4號人員 已經到達!
還需要等 6 個成員!
8號人員 已經到達!
還需要等 5 個成員!
5號人員 已經到達!
還需要等 4 個成員!
6號人員 已經到達!
還需要等 3 個成員!
3號人員 已經到達!
還需要等 2 個成員!
7號人員 已經到達!
還需要等 1 個成員!
2號人員 已經到達!
還需要等 0 個成員!
所有人都到齊了,開會吧!
3. phaser
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
|
import java.util.concurrent.phaser; import java.util.concurrent.timeunit; import java.util.list; import java.util.arraylist; import java.io.file; import java.util.date; public class program{ public static void main(string[] agrs){ phaser phaser= new phaser( 3 ); filesearch system= new filesearch( "c:\\windows" , "log" ,phaser); filesearch apps= new filesearch( "c:\\program files" , "log" ,phaser); filesearch documents= new filesearch( "c:\\documents and settings" , "log" ,phaser); thread systemthread= new thread(system, "system" ); systemthread.start(); thread appsthread= new thread(apps, "apps" ); appsthread.start(); thread documentsthread= new thread(documents, "documents" ); documentsthread.start(); try { systemthread.join(); appsthread.join(); documentsthread.join(); } catch (interruptedexception e) { e.printstacktrace(); } system.out.println( "terminated: " + phaser.isterminated()); } } class filesearch implements runnable{ private string initpath; private string end; private list<string> results; private phaser phaser; public filesearch(string initpath,string end,phaser phaser){ this .initpath=initpath; this .end=end; this .results= new arraylist<string>(); this .phaser=phaser; } private void directoryprocess(file file){ file[] files=file.listfiles(); if (files!= null ){ for ( int i= 0 ;i<files.length;i++){ if (files[i].isdirectory()){ directoryprocess(files[i]); } else { fileprocess(files[i]); } } } } private void fileprocess(file file){ if (file.getname().endswith(end)){ results.add(file.getabsolutepath()); } } private void filterresults(){ list<string> newresults= new arraylist<string>(); long actualdate= new date().gettime(); for ( int i= 0 ;i<results.size();i++){ file file= new file(results.get(i)); long filedate=file.lastmodified(); if (actualdate-filedate<timeunit.milliseconds.convert( 1 ,timeunit.days)){ newresults.add(results.get(i)); } } results=newresults; } private boolean checkresults(){ if (results.isempty()){ system.out.printf( "%s: phase %d: 0 results.\n" ,thread.currentthread().getname(),phaser.getphase()); system.out.printf( "%s: phase %d: end.\n" ,thread.currentthread().getname(),phaser.getphase()); phaser.arriveandderegister(); } else { system.out.printf( "%s: phase %d: %d results.\n" ,thread.currentthread().getname(),phaser.getphase(),results.size()); phaser.arriveandawaitadvance(); return true ; } } private void showinfo() { for ( int i= 0 ; i<results.size(); i++){ file file= new file(results.get(i)); system.out.printf( "%s: %s\n" ,thread.currentthread().getname(),file.getabsolutepath()); } phaser.arriveandawaitadvance(); } @override public void run(){ file file= new file(initpath); if (file.isdirectory()){ directoryprocess(file); } if (!checkresults()){ return ; } filterresults(); if (!checkresults()){ return ; } showinfo(); phaser.arriveandderegister(); system.out.printf( "%s: work completed.\n" ,thread.currentthread().getname()); } } |
運行結果:
apps: phase 0: 4 results.
system: phase 0: 27 results.
希望本文所述對大家java程序設計有所幫助。
原文鏈接:https://blog.csdn.net/kkkkkxiaofei/article/details/19079259