zoukankan      html  css  js  c++  java
  • 并发编程与高并发学习笔记四

    线程封闭
    一,什么是线程封闭
    把对象封装到一个线程里,只有一个线程能看到。这样就算这个对象不是线程安全的,也不会出现线程安全问题
    一,实现线程封闭的方法
    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();
            }
        }
    }
    
    
  • 相关阅读:
    Cxx11 stdchrono库详解
    Oracle中文乱码
    Javascript 编程小技巧总结(部分内容借鉴他人)
    从文档流角度理解浏览器页面渲染引擎对元素定位的解析
    JS重点特性——闭包详解
    用一段JS代码来比较各浏览器的极限内存与运算速度
    前端开发人员需知——浏览器详解
    Js变量定义——fn里 var与不var的区别
    Js文字特效—文字段逐个变色循环
    html5 canvas画图之图形随拖动而复制(有操作指示)
  • 原文地址:https://www.cnblogs.com/inspred/p/9520940.html
Copyright © 2011-2022 走看看