并发编程与高并发学习笔记四

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

 


posted @ 2018-08-22 22:50  inspire0x001  阅读(249)  评论(0编辑  收藏  举报