java并发编程(对于线程内共享变量保险的思考)
上一篇博客讲解了,多个线程之间的互斥和同步的操作,一个是利用了锁的技术;另一个内则是利用了Object的notify和wait来实现同步操作。这篇博客呢,来谈一下对于线程内变量的安全问题。
经典的三层架构,我们都应该比较的熟知,分别是表现层—业务逻辑层——数据访问层。那么问题来了,我们如何来保证我们的业务逻辑层来维持同一个数据库连接对象呢?
<span style="font-family:Comic Sans MS;font-size:18px;">package com.test; import java.util.Random; public class ThreadScopeShareData { //用一个对象来模仿,数据库连接对象操作 private static int data = 0; public static void main(String[] args) { //循环方式共启动8个线程 for(int i=0;i<8;i++){ //启动一个线程 new Thread(new Runnable(){ @Override public void run() { //每次都会读取一个新的随机数,类似于每次都会去数据库获取一个新的操作 data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data :" + data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } } </span>
上述例子总,一共启动了8个线程,每次都会去获取新的随机数,类似于我们每次都会获取新的数据库连接对象操作。针对于上面的例子,可想而知,肯定是不可以的,保证不了线程内变量的安全,那么我们怎么办呢?
想法一
把每个线程的变量单独放置在一个地方,获取的时候,根据线程的标识去获取。说白了就是把每个线程创建的变量单独存起来,找的时候,还找他。因此我们可以采取map的方式来实现,也就这样来操作。
private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
具体看下面的代码
<span style="font-family:Comic Sans MS;font-size:18px;">package com.test; import java.util.HashMap; import java.util.Map; import java.util.Random; public class ThreadScopeShareData { private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>(); public static void main(String[] args) { //共启动2个线程 for(int i=0;i<8;i++){ //启动一个线程 new Thread(new Runnable(){ @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data :" + data); //以当前线程为key值放入到map中,当取值时根据各自的线程取各自的数据 threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } } </span>
上面就是把每个线程创建的变量放置到单独的一个地方,也就是map中,到时候根据线程标识去map中寻找。
想法二
其实在java中已经为我们提供好了类 ThreadLocal<T>,该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get 或set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
有了ThreadLocal,上面的代码就变得容易起来,直接调用相应的set和get方法即可。
<span style="font-family:Comic Sans MS;font-size:18px;">package com.test; import java.util.Random; public class ThreadScopeShareData { //通过ThreadLocal来存取线程内的变量 private static ThreadLocal<Integer> threadData=new ThreadLocal<Integer>(); public static void main(String[] args) { //共启动8个线程 for(int i=0;i<8;i++){ //启动一个线程 new Thread(new Runnable(){ @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data :" + data); //向当前线程中放置相应的局部变量 threadData.set(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ //从当前线程中进行获取放置的线程变量 int data = threadData.get(); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ int data = threadData.get(); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } } </span>
通过ThreadLocal,就可以方便的实现,多个线程之间的变量的安全,但是,我们也发现一个ThreadLocal只能存取一个变量,那么多个变量如何来操作呢?
这时候我们换一个思路不就可以了吗?ThreadLocal可以放置变量,也绝对可以放置对象哈!我们把需要放置的变量封装成一个对象不就可以了吗?或者封装到集合,只要打包就可以了哈!具体的代码小编就不在多说了。
版权声明:本文为博主原创文章,未经博主允许不得转载。