线程封闭
一.什么是线程封闭?
简单来说,使线程间数据不共享的技术称为线程封闭
二.这样的技术有哪些?
1.栈封闭:局部变量
局部变量的固有属性之一就是封闭在执行的线程中,它们位于执行线程的栈中,其他线程无法访问。
why?
下面来详细了解什么是局部变量:
首先,我们知道局部变量定义在方法中,在执行方法时产生,方法执行完毕后销毁,由于这个特性使多个线程访问方法时,方法中的局部变量会被产生拷贝到一份到线程栈单独存在中,而导致局部变量线程间隔离。
下面我们看个例子(看不懂的同学可以先复习下线程池的基本用法):
package Concurrency;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class ThreadLock {
public static int clientTotal=5000;
public static int threadTotal=200;
public static int count=0;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService= Executors.newCachedThreadPool();//线程池
final Semaphore semaphore=new Semaphore(threadTotal);
final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
for(int i=0;i<clientTotal;i++){
executorService.execute(()->{
try{
semaphore.acquire();
count=Mothen();
semaphore.release();
}catch(Exception e){
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println(count);
}
public static int Mothen(){
int a=1;
a++;
return a;
}
}
上面多个线程并发进入方法Mothen中,由于线程封闭,最后结果还是2,可以试一试
2.ThreadLocal技术
前面的栈封闭线程每次进入方法都会新创建变量来放入线程,那有没有什么办法让这个变量只创建一次也可以实现线程封闭的效果呢?
答案是ThreadLocal
下面我们先来看看ThreadLocal如何使用以及效果:
import java.util.concurrent.atomic.AtomicInteger;
public class Text {
private static final AtomicInteger nextId = new AtomicInteger(0);
/*
创建一个线程局部变量,将nextId存入
*/
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.get();
}
};
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
for(int i=0;i<50;i++){
new Thread(new Runnable(){
/*
这里的run方法每次从线程局部变量中取值
*/
@Override
public void run() {
threadId.set(10);
Integer count=get();
count++;
System.out.println(count);
}
}).start();
}
}
}
上面使用ThreadLocal的方法让多个线程都拥有属于自己的对象count互不影响,且目标对象nextId只创建了一次,相比于栈封闭,节约了资源。
下面我们学习ThrealLocal:
1)这个类能使线程中的某个值与保存值得对象关联起来,它提供了get和set方法,这些方法为每个使用该变量得线程都存有一份独立的副本,因此 get总是返回由当前执行线程在调用set时设置的最新值。
2)ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。
3)当某个频繁执行的操作需要一个临时对象,而同时又希望避免在每次执行时重新分配该临时对象,可以用这项技术。
例如:在单线程应用程序中可能会维持一个全局的数据库连接,并在程序启动时初始化这个连接对象,从而避免在调用每个方法时都要传递连接对象,但由于JDBC连接对象不一定是线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的,这时我们可以:通过将JDBC的连接保存到ThreadLocal对象中,这样每个线程都会拥有自己的连接。
下面时ThreadLocal的API:
总结:线程封闭的一般方法有栈封闭(局部变量),ThreadLocal封闭,后者比前者更优(节约资源)