java并发编程(对于线程内共享变量安全的思考)

时间:2021-01-28 18:16:17

 

        上一篇博客讲解了,多个线程之间的互斥和同步的操作,一个是利用了锁的技术;另一个内则是利用了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) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其getset 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。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可以放置变量,也绝对可以放置对象哈!我们把需要放置的变量封装成一个对象不就可以了吗?或者封装到集合,只要打包就可以了哈!具体的代码小编就不在多说了。