使用Synchronized關(guān)鍵字同步類方法
要想解決"臟數(shù)據(jù)"的問題,最簡單的方法就是使用synchronized關(guān)鍵字來使run方法同步,代碼如下public synchronized void run()
2.{
3.
4.}
從上面的代碼可以看出,只要在void和public之間加上synchronized關(guān)鍵字,就可以使run方法同步,也就是說,對于同一個Java類的對象實(shí)例,run方法同時(shí)只能被一個線程調(diào)用,并當(dāng)前的run執(zhí)行完后,才能被其他的線程調(diào)用。即使當(dāng)前線程執(zhí)行到了run方法中的yield方法,也只是暫停了一下。由于其他線程無法執(zhí)行run方法,因此,最終還是會由當(dāng)前的線程來繼續(xù)執(zhí)行。先看看下面的代碼:
sychronized關(guān)鍵字只和一個對象實(shí)例綁定
5.class Test
6.{
7. public synchronized void method()
8. {
9.
10. }
11.}
12.
13.public class Sync implements Runnable
14.{
15. private Test test;
16. public void run()
17. {
18. test.method();
19. }
20. public Sync(Test test)
21. {
22. this.test = test;
23. }
24. public static void main(String[] args) throws Exception
25. {
26. Test test1 = new Test();
27. Test test2 = new Test();
28. Sync sync1 = new Sync(test1);
29. Sync sync2 = new Sync(test2);
30. new Thread(sync1)。start();
31. new Thread(sync2)。start();
32. }
33. }
在Test類中的method方法是同步的。但上面的代碼建立了兩個Test類的實(shí)例,因此,test1和test2的method方法是分別執(zhí)行的。要想讓method同步,必須在建立Sync類的實(shí)例時(shí)向它的構(gòu)造方法中傳入同一個Test類的實(shí)例,如下面的代碼所示:
Sync sync1 = new Sync(test1); 不僅可以使用synchronized來同步非靜態(tài)方法,也可以使用synchronized來同步靜態(tài)方法。如可以按如下方式來定義method方法:
34.class Test
35.{
36. public static synchronized void method() { }
37.}
建立Test類的對象實(shí)例如下:
38.Test test = new Test();
對于靜態(tài)方法來說,只要加上了synchronized關(guān)鍵字,這個方法就是同步的,無論是使用test.method(),還是使用Test.method()來調(diào)用method方法,method都是同步的,并不存在非靜態(tài)方法的多個實(shí)例的問題。
在23種設(shè)計(jì)模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計(jì),也是線程不安全的,下面的代碼是一個線程不安全的單件模式。
39.package test;
40.
41.// 線程安全的Singleton模式
42.class Singleton
43.{
44. private static Singleton sample;
45.
46. private Singleton()
47. {
48. }
49. public static Singleton getInstance()
50. {
51. if (sample == null)
52. {
53. Thread.yield(); // 為了放大Singleton模式的線程不安全性
54. sample = new Singleton();
55. }
56. return sample;
57. }
58.}
59.public class MyThread extends Thread
60.{
61. public void run()
62. {
63. Singleton singleton = Singleton.getInstance();
64. System.out.println(singleton.hashCode());
65. }
66. public static void main(String[] args)
67. {
68. Thread threads[] = new Thread;
69. for (int i = 0; i < threads.length; i++)
70. threads[i] = new MyThread();
71. for (int i = 0; i < threads.length; i++)
72. threads[i].start();
73. }
74.}
在上面的代碼調(diào)用yield方法是為了使單件模式的線程不安全性表現(xiàn)出來,如果將這行去掉,上面的實(shí)現(xiàn)仍然是線程不安全的,只是出現(xiàn)的可能性小得多。
程序的運(yùn)行結(jié)果如下:
25358555
26399554
7051261
29855319
5383406
上面的運(yùn)行結(jié)果可能在不同的運(yùn)行環(huán)境上有所有同,但一般這五行輸出不會完全相同。從這個輸出結(jié)果可以看出,通過getInstance方法得到的對象實(shí)例是五個,而不是我們期望的一個。這是因?yàn)楫?dāng)一個線程執(zhí)行了Thread.yield()后,就將CPU資源交給了另外一個線程。由于在線程之間切換時(shí)并未執(zhí)行到創(chuàng)建Singleton對象實(shí)例的語句,因此,這幾個線程都通過了if判斷,所以,就會產(chǎn)生了建立五個對象實(shí)例的情況(可能創(chuàng)建的是四個或三個對象實(shí)例,這取決于有多少個線程在創(chuàng)建Singleton對象之前通過了if判斷,每次運(yùn)行時(shí)可能結(jié)果會不一樣)。
要想使上面的單件模式變成線程安全的,只要為getInstance加上synchronized關(guān)鍵字即可。代碼如下
75.public static synchronized Singleton getInstance() { }
當(dāng)然,還有更簡單的方法,就是在定義Singleton變量時(shí)就建立Singleton對象,代碼如下
76.private static final Singleton sample = new Singleton();
然后在getInstance方法中直接將sample返回即可。這種方式雖然簡單,但不知在getInstance方法中創(chuàng)建Singleton對象靈活。讀者可以根據(jù)具體的需求選擇使用不同的方法來實(shí)現(xiàn)單件模式。
要想解決"臟數(shù)據(jù)"的問題,最簡單的方法就是使用synchronized關(guān)鍵字來使run方法同步,代碼如下public synchronized void run()
2.{
3.
4.}
從上面的代碼可以看出,只要在void和public之間加上synchronized關(guān)鍵字,就可以使run方法同步,也就是說,對于同一個Java類的對象實(shí)例,run方法同時(shí)只能被一個線程調(diào)用,并當(dāng)前的run執(zhí)行完后,才能被其他的線程調(diào)用。即使當(dāng)前線程執(zhí)行到了run方法中的yield方法,也只是暫停了一下。由于其他線程無法執(zhí)行run方法,因此,最終還是會由當(dāng)前的線程來繼續(xù)執(zhí)行。先看看下面的代碼:
sychronized關(guān)鍵字只和一個對象實(shí)例綁定
5.class Test
6.{
7. public synchronized void method()
8. {
9.
10. }
11.}
12.
13.public class Sync implements Runnable
14.{
15. private Test test;
16. public void run()
17. {
18. test.method();
19. }
20. public Sync(Test test)
21. {
22. this.test = test;
23. }
24. public static void main(String[] args) throws Exception
25. {
26. Test test1 = new Test();
27. Test test2 = new Test();
28. Sync sync1 = new Sync(test1);
29. Sync sync2 = new Sync(test2);
30. new Thread(sync1)。start();
31. new Thread(sync2)。start();
32. }
33. }
在Test類中的method方法是同步的。但上面的代碼建立了兩個Test類的實(shí)例,因此,test1和test2的method方法是分別執(zhí)行的。要想讓method同步,必須在建立Sync類的實(shí)例時(shí)向它的構(gòu)造方法中傳入同一個Test類的實(shí)例,如下面的代碼所示:
Sync sync1 = new Sync(test1); 不僅可以使用synchronized來同步非靜態(tài)方法,也可以使用synchronized來同步靜態(tài)方法。如可以按如下方式來定義method方法:
34.class Test
35.{
36. public static synchronized void method() { }
37.}
建立Test類的對象實(shí)例如下:
38.Test test = new Test();
對于靜態(tài)方法來說,只要加上了synchronized關(guān)鍵字,這個方法就是同步的,無論是使用test.method(),還是使用Test.method()來調(diào)用method方法,method都是同步的,并不存在非靜態(tài)方法的多個實(shí)例的問題。
在23種設(shè)計(jì)模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計(jì),也是線程不安全的,下面的代碼是一個線程不安全的單件模式。
39.package test;
40.
41.// 線程安全的Singleton模式
42.class Singleton
43.{
44. private static Singleton sample;
45.
46. private Singleton()
47. {
48. }
49. public static Singleton getInstance()
50. {
51. if (sample == null)
52. {
53. Thread.yield(); // 為了放大Singleton模式的線程不安全性
54. sample = new Singleton();
55. }
56. return sample;
57. }
58.}
59.public class MyThread extends Thread
60.{
61. public void run()
62. {
63. Singleton singleton = Singleton.getInstance();
64. System.out.println(singleton.hashCode());
65. }
66. public static void main(String[] args)
67. {
68. Thread threads[] = new Thread;
69. for (int i = 0; i < threads.length; i++)
70. threads[i] = new MyThread();
71. for (int i = 0; i < threads.length; i++)
72. threads[i].start();
73. }
74.}
在上面的代碼調(diào)用yield方法是為了使單件模式的線程不安全性表現(xiàn)出來,如果將這行去掉,上面的實(shí)現(xiàn)仍然是線程不安全的,只是出現(xiàn)的可能性小得多。
程序的運(yùn)行結(jié)果如下:
25358555
26399554
7051261
29855319
5383406
上面的運(yùn)行結(jié)果可能在不同的運(yùn)行環(huán)境上有所有同,但一般這五行輸出不會完全相同。從這個輸出結(jié)果可以看出,通過getInstance方法得到的對象實(shí)例是五個,而不是我們期望的一個。這是因?yàn)楫?dāng)一個線程執(zhí)行了Thread.yield()后,就將CPU資源交給了另外一個線程。由于在線程之間切換時(shí)并未執(zhí)行到創(chuàng)建Singleton對象實(shí)例的語句,因此,這幾個線程都通過了if判斷,所以,就會產(chǎn)生了建立五個對象實(shí)例的情況(可能創(chuàng)建的是四個或三個對象實(shí)例,這取決于有多少個線程在創(chuàng)建Singleton對象之前通過了if判斷,每次運(yùn)行時(shí)可能結(jié)果會不一樣)。
要想使上面的單件模式變成線程安全的,只要為getInstance加上synchronized關(guān)鍵字即可。代碼如下
75.public static synchronized Singleton getInstance() { }
當(dāng)然,還有更簡單的方法,就是在定義Singleton變量時(shí)就建立Singleton對象,代碼如下
76.private static final Singleton sample = new Singleton();
然后在getInstance方法中直接將sample返回即可。這種方式雖然簡單,但不知在getInstance方法中創(chuàng)建Singleton對象靈活。讀者可以根據(jù)具體的需求選擇使用不同的方法來實(shí)現(xiàn)單件模式。