線程安全的本質體現在兩個方面,
?。磷兞堪踩憾嗑€程同時運行一段代碼
?。戮€程同步:一個線程還沒執行完,另一個線程又進來接著執行。
看個簡單的例子。
- public class ThreadSafe implements java.lang.Runnable {
-
- int num = 1;
- public void run() {
- for (int i = 0; i < 3; i++) {
- num = num + 1;
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
- }
- }
-
- }
public class ThreadSafe implements java.lang.Runnable {
int num = 1;
public void run() {
for (int i = 0; i < 3; i++) {
num = num + 1;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
}
}
}
TestMan.java 寫道
package com.java.thread.test;
public class TestMan {
public static void main(String[] args) {
Runnable safe=new ThreadSafe();
Thread thread1=new Thread(safe,"thread1");
Thread thread2=new Thread(safe,"thread2");
thread1.start();
thread2.start();
}
}
運行結果
num is value +===thread2---------3
num is value +===thread1---------4
num is value +===thread2---------5
num is value +===thread1---------6
num is value +===thread1---------7
num is value +===thread2---------7
很明顯是錯誤的,應為兩個線程共享同一個變量。這里就是變量的安全問題。
解決辦法:
1拋棄單實例,多線程的方式,用多實例,多線程的方式,這樣就和單線程是一個樣了,不會出錯,但是是最接近傳統的編程模式
2不要用類的實例變量,經可能把變量封裝到方法內部。
1類的解決辦法的代碼。
- public class TestMan {
- public static void main(String[] args) {
- Runnable safe=new ThreadSafe();
- Runnable safe2=new ThreadSafe();
- Thread thread1=new Thread(safe,"thread1");
- Thread thread2=new Thread(safe2,"thread2");
- thread1.start();
- thread2.start();
- }
-
- }
public class TestMan {
public static void main(String[] args) {
Runnable safe=new ThreadSafe();
Runnable safe2=new ThreadSafe();
Thread thread1=new Thread(safe,"thread1");
Thread thread2=new Thread(safe2,"thread2");
thread1.start();
thread2.start();
}
}
運行結果
num is value +===thread1---------2
num is value +===thread2---------2
num is value +===thread1---------3
num is value +===thread2---------3
num is value +===thread1---------4
num is value +===thread2---------4
2類解決辦法的代碼
- public class ThreadSafe implements java.lang.Runnable {
-
- public void run() {
- int num = 1;
- for (int i = 0; i < 3; i++) {
- num = num + 1;
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
- }
- }
-
- }
public class ThreadSafe implements java.lang.Runnable {
public void run() {
int num = 1;
for (int i = 0; i < 3; i++) {
num = num + 1;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
}
}
}
- public class TestMan {
- public static void main(String[] args) {
- Runnable safe=new ThreadSafe();
-
- Thread thread1=new Thread(safe,"thread1");
- Thread thread2=new Thread(safe,"thread2");
- thread1.start();
- thread2.start();
- }
-
- }
public class TestMan {
public static void main(String[] args) {
Runnable safe=new ThreadSafe();
Thread thread1=new Thread(safe,"thread1");
Thread thread2=new Thread(safe,"thread2");
thread1.start();
thread2.start();
}
}
運行結果
num is value +===thread2---------2
num is value +===thread1---------2
num is value +===thread1---------3
num is value +===thread2---------3
num is value +===thread1---------4
num is value +===thread2---------4
這兩種辦法,比較推薦適用第二個辦法,就是把變量經可能的封裝到風發內部,這樣他們就是線程的私有變量了。另外,從jdk1.2后,推出了
threadlocal 對象,它作為線程的一個局部變量,可以為每個線程創建一個副本,用來保存每個線程的屬性,各是各的,互不干擾。單每個
threadlocal變量只能保存一個變量,假如有多個變量要保存,那么就要寫多個threadlocal對象。
我們把代碼改寫一下。
- public class ThreadSafe implements java.lang.Runnable {
- ThreadLocal<Integer> local=new ThreadLocal<Integer>();
- public void run() {
- for (int i = 0; i < 3; i++) {
- if(local.get()==null){
- local.set(new Integer(1));
- }
- int num=local.get().intValue();
- num=num+1;
- local.set(new Integer(num));
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + local.get().intValue());
- }
- }
-
- }
public class ThreadSafe implements java.lang.Runnable {
ThreadLocal<Integer> local=new ThreadLocal<Integer>();
public void run() {
for (int i = 0; i < 3; i++) {
if(local.get()==null){
local.set(new Integer(1));
}
int num=local.get().intValue();
num=num+1;
local.set(new Integer(num));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + local.get().intValue());
}
}
}
- public class TestMan {
- public static void main(String[] args) {
- Runnable safe=new ThreadSafe();
- Thread thread1=new Thread(safe,"thread1");
- Thread thread2=new Thread(safe,"thread2");
- thread1.start();
- thread2.start();
- }
-
- }
public class TestMan {
public static void main(String[] args) {
Runnable safe=new ThreadSafe();
Thread thread1=new Thread(safe,"thread1");
Thread thread2=new Thread(safe,"thread2");
thread1.start();
thread2.start();
}
}
運行結果
num is value +===thread2---------2
num is value +===thread1---------2
num is value +===thread1---------3
num is value +===thread2---------3
num is value +===thread1---------4
num is value +===thread2---------4
結果是一樣的,所以這里變量安全有3個辦法可以解決。
然后在說說線程的同步的問題。我們看上面的運行結果。
num is value +===thread2---------2
num is value +===thread1---------2
num is value +===thread1---------3
num is value +===thread2---------3
num is value +===thread1---------4
num is value +===thread2---------4
就 可以看出他們不是線程同步的,是thread1和thread2在交替執行的。在有些情況下,要求一段代碼在運行的過程中是一個不可分割的實
體,就是原子的。就是說當已經有線程在執行這段代碼的時候,其他的線程必須等待他執行完畢后才能竟來執行,這就是所謂的線程同步。
java通過同步鎖來執行線程的同步和等待,也就是說,要不間斷執行的代碼需要放在synchronized關鍵字標識的代碼塊中??梢杂脕硇揎棿?
碼塊,也可以修飾方法。
- public class ThreadSafe implements java.lang.Runnable{
- public synchronized void run() {
- int num = 1;
- for (int i = 0; i < 3; i++) {
- num = num + 1;
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
- }
- }
-
-
- }
public class ThreadSafe implements java.lang.Runnable{
public synchronized void run() {
int num = 1;
for (int i = 0; i < 3; i++) {
num = num + 1;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num is value +==="+Thread.currentThread().getName()+"---------" + num);
}
}
}
- public class TestMan {
- public static void main(String[] args) {
- Runnable safe=new ThreadSafe();
- Thread thread1=new Thread(safe,"thread1");
- Thread thread2=new Thread(safe,"thread2");
- thread1.start();
- thread2.start();
- }
-
- }
public class TestMan {
public static void main(String[] args) {
Runnable safe=new ThreadSafe();
Thread thread1=new Thread(safe,"thread1");
Thread thread2=new Thread(safe,"thread2");
thread1.start();
thread2.start();
}
}
運行結果
um is value +===thread1---------2
num is value +===thread1---------3
num is value +===thread1---------4
num is value +===thread2---------2
num is value +===thread2---------3
num is value +===thread2---------4
可以看到thread1運行結束后thread2才開始運行的。代碼還可以這么寫
- public class ThreadSafe implements java.lang.Runnable {
- public void run() {
- int num = 1;
- synchronized (this) {
- for (int i = 0; i < 3; i++) {
- num = num + 1;
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("num is value +==="
- + Thread.currentThread().getName() + "---------" + num);
- }
- }
- }
-
- }
public class ThreadSafe implements java.lang.Runnable {
public void run() {
int num = 1;
synchronized (this) {
for (int i = 0; i < 3; i++) {
num = num + 1;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num is value +==="
+ Thread.currentThread().getName() + "---------" + num);
}
}
}
}
在啟用同步鎖機制以后,需要避免
1無線等待,,線程B等待線程A執行完畢,然后線程A確進入了死循環。
2循環等待:兩個線程相互調用,都要求要同步執行,這個時候就先會循環等待,我等你執行,你也在等我執行,這個時候就死鎖了
posted on 2010-06-05 17:23
凌宇 閱讀(3609)
評論(0) 編輯 收藏