线程封闭
一,什么是线程封闭
把对象封装到一个线程里,只有一个线程能看到。这样就算这个对象不是线程安全的,也不会出现线程安全问题
一,实现线程封闭的方法
Ad-hoc线程封闭:程序控制实现,最糟糕,忽略
堆栈封闭:局部变量,无并发问题
多个线程访问一个方法时,方法中的局部变量会被拷贝一份到线程的栈中,所以局部变量是不会被多个线程访问的,
也就不会出现并发问题了。能用局部变量的时候,就不用全局变量
ThreadLocal线程封闭:特别好的封闭方法
ThreadLocal内部维护了一个Map,Map的key是线程的名称,Map的value就是要封闭的对象,ThreadLocal利用
Map实现了对象的封闭
二,应用
链接数据库的Connection对象,其实这个对象并没有被要求是线程安全的
当线程从连接池中获取了一个链接对象,使用完后将连接对象返回给连接池,由于大多数请求都是由单线程采用同步的方式
来处理的,并且在Connection对象返回之前,连接池不会将它分配给其他线程,因此这种管理模式,在处理请求时隐含的
将Connection对象封闭在线程里面,这样我们使用的Connection对象本身不是线程安全的,但是通过线程封闭也做到了
线程安全
三,ThreadLocal对象的使用
public class RequestHolder {
private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();
public static void add(Long id) {
requestHolder.set(id);
}
public static Long getId() {
return requestHolder.get();
}
public static void remove() {
requestHolder.remove();
}
}
权限管理课程总结下
----------------------------------------------------------
线程不安全类的写法
一,StringBuffer和StringBuilder
StringBuffer对象的所有方法都加了synchronized关键字,是线程安全的
二,SimpleDateFormat和JodaTime
1.SimpleDateFormat不是线程安全的,不要做全局变量,多线程下会报错
2.JodaTime是线程安全的
三,ArrayList,HashSet等
四,扩展
通常来说:那些先检查再执行的代码都是不安全的,类似下面的代码。
if(condition(a)){
handle(a);
}
当两个线程同时走到判断条件那一步,在往后执行,就可能出现线程不安全的问题
//线程不安全
public class DateSimpleExample {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
//请求总数
public static int clientTotal = 5000;
//同时并发执行的线程数
public static int threadTotal = 200;
public static void main(String[] args) {
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();
update();
//释放信号灯
semaphore.release();
}catch (InterruptedException e){
System.out.println("exception");
e.printStackTrace();
}
//闭锁,每执行一次add()操作,请求数就减一
countDownLatch.countDown();
});
}
//等待上面的线程都执行完毕,countDown的值减为0,然后才向下执行主线程
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//关闭线程池
executorService.shutdown();
}
private static void update(){
try {
simpleDateFormat.parse("20180820");
} catch (ParseException e) {
e.printStackTrace();
}
}
}
//线程安全的
public class DateSimpleExampl2 {
//请求总数
public static int clientTotal = 5000;
//同时并发执行的线程数
public static int threadTotal = 200;
public static void main(String[] args) {
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();
update();
//释放信号灯
semaphore.release();
}catch (InterruptedException e){
System.out.println("exception");
e.printStackTrace();
}
//闭锁,每执行一次add()操作,请求数就减一
countDownLatch.countDown();
});
}
//等待上面的线程都执行完毕,countDown的值减为0,然后才向下执行主线程
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//关闭线程池
executorService.shutdown();
}
private static void update(){
//使用时作为局部变量
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
try {
simpleDateFormat.parse("20180820");
} catch (ParseException e) {
e.printStackTrace();
}
}
}