假定現在有個公共的變量data,有不同的線程都可以去操作它,如果在不同的線程對data操作完成后再去取這個data,那末肯定會出現線程間的數據混亂問題,由于A線程在取data數據前可能B線程又對其進行了修改,下面寫個程序來講明1下該問題:
public class ThreadScopeShareData {
private static int data = 0;//公共的數據
public static void main(String[] args) {
for(int i = 0; i < 2; i ++) { //開啟兩個線程
new Thread(new Runnable() {
@Override
public void run() {
int temp = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出來為了看效果
data = temp; //操作數據:賦新值
new TestA().getData();
new TestB().getData();
}
}).start();
}
}
static class TestA {
public void getData() {
System.out.println("A get data from " + Thread.currentThread().getName() + ": " + data);//取出公共數據data
}
}
static class TestB {
public void getData() {
System.out.println("B get data from " + Thread.currentThread().getName() + ": " + data);
}
}
}
來看1下打印出來的結果:
Thread-0 has put a data: ⑴885917900
Thread⑴ has put a data: ⑴743455464
A get data from Thread-0: ⑴743455464
A get data from Thread⑴: ⑴743455464
B get data from Thread⑴: ⑴743455464
B get data from Thread-0: ⑴743455464
從結果中可以看出,兩次對data賦的值確切不1樣,但是兩個線程最后打印出來的都是最后賦的那個值,說明Thread-0拿出的數據已不對了,這就是線程間同享數據帶來的問題。
固然,我們完全可使用synchronized關鍵字將run()方法中的幾行代碼給套起來,這樣每一個線程各自履行完,打印出各自的信息,這是沒問題的,確切可以解決上面的線程間同享數據問題。但是,這是以其他線程被阻塞為代價的,即Thread-0在履行的時候,Thread⑴就被阻塞了,必須等待Thread-0履行完了才能履行。
那末如果我想兩個線程同時跑,并且互不影響各自取出的值,該怎樣辦呢?這也是本文所要總結的重點,解決該問題的思想是:雖然現在都在操作公共數據data,但是不同的線程本身對這個data要保護1個副本,這個副本不是線程間所同享的,而是每一個線程所獨有的,所以不同線程中所保護的data是不1樣的,最后取的時候,是哪一個線程,我就從哪一個線程中取該data。
基于上面這個思路,我再把上面的程序做1修改,以下:
public class ThreadScopeShareData {
private static int data = 0;//公共的數據
//定義1個Map以鍵值對的方式存儲每一個線程和它對應的數據,即Thread:data
private static Map<Thread, Integer> threadData = Collections.synchronizedMap(new HashMap<Thread, Integer>());
public static void main(String[] args) {
for(int i = 0; i < 2; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
int temp = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出來為了看效果
threadData.put(Thread.currentThread(), temp); //向Map中存入本線程data數據的1個副本
data = temp; //操作數據:賦新值
new TestA().getData();
new TestB().getData();
}
}).start();
}
}
static class TestA {
public void getData() {
System.out.println("A get data from " + Thread.currentThread().getName() + ": "
+ threadData.get(Thread.currentThread())); //取出各線程保護的那個副本
}
}
static class TestB {
public void getData() {
System.out.println("B get data from " + Thread.currentThread().getName() + ": "
+ threadData.get(Thread.currentThread()));
}
}
}
上面程序中保護了1個Map,鍵值對分別是線程和它的數據,那末在操作data的時候,先把各自的數據保存到這個Map中,這樣每一個線程保存的肯定不同,當再取的時候,根據當前線程對象作為key來取出對應的data副本,這樣不同的線程之間就不會相互影響了。這個HashMap也需要包裝1下,由于HashMap是非線程安全的,上面的程序中,不同的線程有對HashMap進行寫操作,就有可能產生并提問題,所以也要包裝1下。最后來看1下履行結果:
Thread-0 has put a data: 1817494992
Thread⑴ has put a data: ⑴189758355
A get data from Thread-0: 1817494992
B get data from Thread⑴: ⑴189758355
A get data from Thread-0: 1817494992
B get data from Thread⑴: ⑴189758355
就是線程范圍內同享數據,即同1個線程里面這個數據是同享的,線程間是不同享的。
這讓我聯想到了學習數據庫的時候用到的ThreadLocal,操作數據庫需要connection,如果當前線程中有就拿當前線程中存的connection,否則就新建1個放到當前線程中,這樣就不會出現問題,由于每一個線程本身同享了1個connection,它不是線程間同享的。這也很好理解,這個connection肯定不能同享,假定A和B用戶都拿到這個connection并開啟了事務,現在A開始轉賬了,但是錢還沒轉好,B轉好了關閉了事務,那末A那邊就出問題了。
線程范圍內同享數據的問題就總結這么多吧~
相干瀏覽:http://blog.csdn.net/column/details/bingfa.html
—–樂于分享,共同進步!
—–更多文章請看:http://blog.csdn.net/eson_15