当多个线程操作同一个共有数据时,一个线程对共有数据的改变会影响到另一个线程。比如下面这个例子:两个线程调用同一个对象的的方法,一个线程的执行结果会影响另一个线程。
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 }
线程: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只是代为封装了一下。