java多线程与线程并发四:线程范围内的共享数据

当多个线程操作同一个共有数据时,一个线程对共有数据的改变会影响到另一个线程。比如下面这个例子:两个线程调用同一个对象的的方法,一个线程的执行结果会影响另一个线程。

 1 package com.sky.thread;
 2 
 3 public class TestThreadLocal {
 4     public static void main(String[] args) {
 5         final ThreadLocalBiz biz = new ThreadLocalBiz();
 6         new Thread(new Runnable() {
 7 
 8             @Override
 9             public void run() {
10                 while (true) {
11                     try {
12                         Thread.sleep(500);
13                     } catch (InterruptedException e) {
14                         // TODO Auto-generated catch block
15                         e.printStackTrace();
16                     }
17                     biz.add();
18                     
19                 }
20             }
21         }).start();
22         new Thread(new Runnable() {
23 
24             @Override
25             public void run() {
26                 while (true) {
27                     try {
28                         Thread.sleep(500);
29                     } catch (InterruptedException e) {
30                         // TODO Auto-generated catch block
31                         e.printStackTrace();
32                     }
33                     biz.add();
34                     
35                 }
36             }
37         }).start();
38     }
39 }
40 
41 class ThreadLocalBiz {
42     private int num = 0;
43 
44     public synchronized void add() {
45         num++;
46         System.out.println("线程:" + Thread.currentThread().getName() + "num:"
47                 + num);
48     }
49 }
线程结果互相影响

执行的结果是:

线程:Thread-0num:1
线程:Thread-1num:2
线程:Thread-0num:3
线程:Thread-1num:4
执行结果

可见,Thread-0执行完以后,num的值是1。当Thread-1开始执行时,num的值对它而言是1而不是一开始的0,所以在Thread-1执行完以后,num的值变成了2。

如果我们想让两个线程在操作同一个共有数据时,互相的操作结果互不影响该怎么办?

这个时候可以使用ThreadLocal。先看下面的代码:

 1 package com.sky.thread;
 2 
 3 public class TestThreadLocal {
 4     
 5     
 6     public static void main(String[] args) {
 7         new Thread(new Runnable() {
 8 
 9             @Override
10             public void run() {
11                 while (true) {
12                     try {
13                         Thread.sleep(500);
14                     } catch (InterruptedException e) {
15                         // TODO Auto-generated catch block
16                         e.printStackTrace();
17                     }
18                     
19                     ThreadLocalBiz.getBiz().add();
20                     
21                 }
22             }
23         }).start();
24         new Thread(new Runnable() {
25 
26             @Override
27             public void run() {
28                 while (true) {
29                     try {
30                         Thread.sleep(1000);
31                     } catch (InterruptedException e) {
32                         // TODO Auto-generated catch block
33                         e.printStackTrace();
34                     }
35                     
36                     ThreadLocalBiz.getBiz().add();
37                     
38                 }
39             }
40         }).start();
41     }
42 }
43 
44 class ThreadLocalBiz {
45     private int num = 0;
46     public static ThreadLocal<ThreadLocalBiz> LocalBiz = new ThreadLocal<ThreadLocalBiz>();
47     
48     private ThreadLocalBiz(){}
49     public static ThreadLocalBiz getBiz(){
50         ThreadLocalBiz biz = LocalBiz.get();
51         if (biz == null) {
52             biz = new ThreadLocalBiz();
53             LocalBiz.set(biz);
54         }
55         return biz;
56     }
57     public void add() {
58         num++;
59         System.out.println("线程:" + Thread.currentThread().getName() + ",num:"
60                 + num);
61     }
62     
63 }
View Code
线程:Thread-0,num:1
线程:Thread-1,num:1
线程:Thread-0,num:2
线程:Thread-0,num:3
线程:Thread-1,num:2
执行结果

 

这段代码的基本思路是,为不同的线程创建单独的实例。比如上面这段代码,有两个线程调用的ThreadLocalBiz的add方法,那么一共创建的两个ThreadLocalBiz实例。每个线程操作自己的专属实例,自然就不会相互干扰了。

在这段代码中,getBiz和add方法都没有加synchronized,就是因为单个实例只对应单个线程,不存在并发问题,自然也就不用加互斥锁了。

现在来仔细说说ThreadLocal。

ThreadLocal相当于一个Map。以当前线程作为key。

下面的代码

ThreadLocal<ThreadLocalBiz> LocalBiz = new ThreadLocal<ThreadLocalBiz>();
LocalBiz.set(new ThreadLocalBiz());
LocalBiz.get();

就相当于

Map<Thread, ThreadLocalBiz> ThreadMap = new HashMap<Thread, ThreadLocalBiz>();
ThreadMap.put(Thread.currentThread(), new ThreadLocalBiz());
ThreadMap.get(Thread.currentThread());

可见,ThreadLocal完全可以被Map代替。java只是代为封装了一下。

 

posted @ 2014-04-07 19:28  百里弈  阅读(255)  评论(0编辑  收藏  举报