線程安全的本質(zhì)體現(xiàn)在兩個(gè)方面,
A變量安全:多線程同時(shí)運(yùn)行一段代碼
B線程同步:一個(gè)線程還沒(méi)執(zhí)行完,另一個(gè)線程又進(jìn)來(lái)接著執(zhí)行。
看個(gè)簡(jiǎn)單的例子。
- 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 寫(xiě)道
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();
}
}
運(yùn)行結(jié)果
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
很明顯是錯(cuò)誤的,應(yīng)為兩個(gè)線程共享同一個(gè)變量。這里就是變量的安全問(wèn)題。
解決辦法:
1拋棄單實(shí)例,多線程的方式,用多實(shí)例,多線程的方式,這樣就和單線程是一個(gè)樣了,不會(huì)出錯(cuò),但是是最接近傳統(tǒng)的編程模式
2不要用類的實(shí)例變量,經(jīng)可能把變量封裝到方法內(nèi)部。
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();
}
}
運(yùn)行結(jié)果
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();
}
}
運(yùn)行結(jié)果
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
這兩種辦法,比較推薦適用第二個(gè)辦法,就是把變量經(jīng)可能的封裝到風(fēng)發(fā)內(nèi)部,這樣他們就是線程的私有變量了。另外,從jdk1.2后,推出了
threadlocal 對(duì)象,它作為線程的一個(gè)局部變量,可以為每個(gè)線程創(chuàng)建一個(gè)副本,用來(lái)保存每個(gè)線程的屬性,各是各的,互不干擾。單每個(gè)
threadlocal變量只能保存一個(gè)變量,假如有多個(gè)變量要保存,那么就要寫(xiě)多個(gè)threadlocal對(duì)象。
我們把代碼改寫(xiě)一下。
- 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();
}
}
運(yùn)行結(jié)果
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
結(jié)果是一樣的,所以這里變量安全有3個(gè)辦法可以解決。
然后在說(shuō)說(shuō)線程的同步的問(wèn)題。我們看上面的運(yùn)行結(jié)果。
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在交替執(zhí)行的。在有些情況下,要求一段代碼在運(yùn)行的過(guò)程中是一個(gè)不可分割的實(shí)
體,就是原子的。就是說(shuō)當(dāng)已經(jīng)有線程在執(zhí)行這段代碼的時(shí)候,其他的線程必須等待他執(zhí)行完畢后才能竟來(lái)執(zhí)行,這就是所謂的線程同步。
java通過(guò)同步鎖來(lái)執(zhí)行線程的同步和等待,也就是說(shuō),要不間斷執(zhí)行的代碼需要放在synchronized關(guān)鍵字標(biāo)識(shí)的代碼塊中。可以用來(lái)修飾代
碼塊,也可以修飾方法。
- 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();
}
}
運(yùn)行結(jié)果
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運(yùn)行結(jié)束后thread2才開(kāi)始運(yùn)行的。代碼還可以這么寫(xiě)
- 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);
}
}
}
}
在啟用同步鎖機(jī)制以后,需要避免
1無(wú)線等待,,線程B等待線程A執(zhí)行完畢,然后線程A確進(jìn)入了死循環(huán)。
2循環(huán)等待:兩個(gè)線程相互調(diào)用,都要求要同步執(zhí)行,這個(gè)時(shí)候就先會(huì)循環(huán)等待,我等你執(zhí)行,你也在等我執(zhí)行,這個(gè)時(shí)候就死鎖了
posted on 2010-06-05 17:23
凌宇 閱讀(3609)
評(píng)論(0) 編輯 收藏