java ThreadLocal保护线程的局部变量
如何保证java局部变量的安全?
一、什么是线程中的局部变量
1.区分线程类中的局部变量和方法中的局部变量
我的理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class varThread implements Runnable { private int i = 0; // 局部变量 public void run() { // TODO Auto-generated method stub int i = 0; // run方法中的局部变量 while (true) { i++; System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (50 == i) { break; } } } } |
2.局部变量
多个线程对同一个对象的局部变量进行操作时,它们对该局部变量是彼此影响的。也就是说,一个线程对局部变量的改变会影响到另一个线程。
所以,如果在线程类中定义一个局部变量,那就是你希望和其他的线程一起共享这个局部变量。
尝试运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package testVar; public class test { public static void main(String[] args) { varThread varThread = new varThread(); Thread thread1 = new Thread(varThread); Thread thread2 = new Thread(varThread); thread1.start(); thread2.start(); } } class varThread implements Runnable { private int i = 0; // 局部变量 public void run() { // TODO Auto-generated method stub while (true) { i++; System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (50 == i) { break; } } } } |
结果为:
1 2 3 4 |
1 2 3 ...... |
这说明thread1和thread2共享varThread对象中的局部变量i。
这样写其实是不安全的,变量i不具有原子性,在并发条件下可能会发生错误。应该使用AtomicInteger。
2.方法中的局部变量
每当启动一个新线程时,java虚拟机都会为它分配一个java栈,java栈上的所有数据都是此线程私有的。任何线程都不能访问另一个线程的栈数据,因此我们不需要考虑多线程情况下栈数据的访问同步问题。
当一个线程调用一个方法的时候,方法的局部变量保存在调用线程的java栈的桢中,只有一个线程能访问那些局部变量,即调用方法的线程。
每启动一条线程,都会分配线程自己的栈。这个线程里面所涉及的对象方法,每个方法由一个栈帧,用来存放方法中的局部变量,中间结果和操作数。也就是说,方法中的局部变量在不同线程的栈帧中,所以不会相互影响。
所以,如果在线程类中的run方法中定义一个局部变量,那你肯定不希望有别的线程可以操作它。
尝试运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package testVar; public class test { public static void main(String[] args) { varThread varThread = new varThread(); Thread thread1 = new Thread(varThread); Thread thread2 = new Thread(varThread); thread1.start(); thread2.start(); } } class varThread implements Runnable { public void run() { // TODO Auto-generated method stub int i = 0; // 方法中的局部变量 while (true) { i++; System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (50 == i) { break; } } } } |
结果为:
1 2 3 4 5 |
1 1 2 2 ...... |
这说明thread1和thread2都有自己私有的局部变量i,彼此之间不能共享。
二、如何保证局部变量的安全?
保证局部变量的安全,其实就是不希望当前线程的局部变量能够被其他线程访问/操作,即实现局部变量的私有化。
但是,如上文所说,如果在线程类中定义一个局部变量,基本上就是希望线程之间能够共享这个成员变量。为了实现局部变量的私有化,必须做一些处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package testVar; public class test { public static void main(String[] args) { varThread varThread = new varThread(); Thread thread1 = new Thread(varThread); Thread thread2 = new Thread(varThread); thread1.start(); thread2.start(); } } class varThread implements Runnable { private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() { private int i = 0; protected Integer initialValue() { return i; } }; public void run() { // TODO Auto-generated method stub while (true) { Integer k = threadLocal.get(); System.out.println(k); threadLocal.set(k + 1); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (50 == k) { break; } } } } |
结果为:
1 2 3 4 5 |
1 1 2 2 ...... |
这证明局部变量i已经被当前线程私有化了。
使用ThreadLocal类,可以定义局部变量的数据类型,实现局部变量的私有化。同时,取值和赋值都需要通过ThreadLocal提供的方法实现。
主要用到的基本上就是initialValue、get、set、remove四个方法,顾名思义。
三、总结
暂时还没有遇上使用ThreadLocal的场景,回头再看看。