并发编程与高并发学习笔记四
线程封闭
一,什么是线程封闭
把对象封装到一个线程里,只有一个线程能看到。这样就算这个对象不是线程安全的,也不会出现线程安全问题
一,实现线程封闭的方法
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(); } } }