Java并发JUC工具类

Java并发JUC工具类

什么是CountDownLatch?底层原理是什么?

1、CountDownLatch底层也是由AQS,用来同步一个或多个任务的常用并发工具类,强制它们等待由其他任务执行的一组操作完成。
2、底层原理:其底层是由AQS提供支持,所以其数据结构可以参考AQS的数据结构,而AQS的数据结构核心就是两个虚拟队列:同步队列sync queue和条件队列condition queue,不同的条件会有不同的条件队列。CountDownLatch典型的用法是将一个程序分为n个互相独立的可解决任务,并创建值为n的CountDownLatch。当每一个任务完成时,都会在这个锁存器上调用countDown,等待问题被解决的任务调用这个锁存器的await,将他们自己拦住,直至锁存器计数结束
3、CountDownLatch一次可以唤醒几个任务

CountDownLatch有哪些主要方法?

await():此函数将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
countDown():此函数将递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

例子

写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * 使用CountDownLatch代替wait notify好处是通讯方式简单,不涉及锁定;Count值为0时,当前线程继续执行
 */
public class T3 {
   volatile List list = new ArrayList();
    public void add(int i){
        list.add(i);
    }
    public int getSize(){
        return list.size();
    }
    public static void main(String[] args) {
        T3 t = new T3();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        new Thread(() -> {
           System.out.println("t2 start");
           if(t.getSize() != 5){
               try {
                   countDownLatch.await();
                   System.out.println("t2 end");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        }, "t2").start();
        new Thread(()->{
           System.out.println("t1 start");
           for (int i = 0; i < 9; i++){
               t.add(i);
               System.out.println("add" + i);
               if(t.getSize() == 5){
                   System.out.println("countdown is open");
                   countDownLatch.countDown();
               }
           }
           System.out.println("t1 end");
        }, "t1").start();
    }
}

什么是CyclicBarrier?

CyclicBarrier通常称为循环屏障。它和CountDownLatch很相似,都可以使线程先等待然后再执行。不过CountDownLatch是使一批线程等待另一批线程执行完后再执行,而CyclicBarrier只是使等待的线程达到一定数目后再让它们继续执行。
1、每调用一次await()方法都将使阻塞的线程数+1;只有阻塞的线程数达到设定值时屏障才会打开;允许阻塞的所有线程继续执行;
2、CyclicBarrier的计数器可以重置而CountDownLatch不行;这意味着CyclicBarrier实例可以被重复使用而CountDownLatch只能被使用一次。而这也是循环屏障循环二字的语义所在。

例子1,简单使用CyclicBarrier

公司组织旅游,一共10个人,中午到饭点了,需要等到10个人都到了才能开饭,先到的人坐那等着。

public class CyclicBarrierDemo1 {
    public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
    public static class T extends Thread {
        int sleep;
        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }
        @Override
        public void run() {
            try {
                //休眠,即模拟先后到达餐桌的时间
                TimeUnit.SECONDS.sleep(sleep);
                long starTime = System.currentTimeMillis();
                //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
                cyclicBarrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 10; i++) {
            new T("员工" + i, i).start();
        }
    }
}
// 输出结果
员工10,sleep:10 等待了0(ms),开始吃饭了!
员工1,sleep:1 等待了8996(ms),开始吃饭了!
员工7,sleep:7 等待了2998(ms),开始吃饭了!
员工9,sleep:9 等待了1000(ms),开始吃饭了!
员工6,sleep:6 等待了3997(ms),开始吃饭了!
员工4,sleep:4 等待了6000(ms),开始吃饭了!
员工5,sleep:5 等待了4999(ms),开始吃饭了!
员工2,sleep:2 等待了7999(ms),开始吃饭了!
员工3,sleep:3 等待了7002(ms),开始吃饭了!
员工8,sleep:8 等待了1999(ms),开始吃饭了!

例子2,重复使用CyclicBarrier

对示例1进行改造一下,吃饭完毕之后所有人都去车上,待所有人都到车上之后,驱车去下一景点玩。

public class CyclicBarrierDemo2 {
    public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
    public static class T extends Thread {
        int sleep;
        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }
        //等待吃饭
        void eat() {
            try {
                //休眠,即模拟先后到达餐桌的时间
                TimeUnit.SECONDS.sleep(sleep);
                long starTime = System.currentTimeMillis();
                //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
                cyclicBarrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");                
                //休眠,模拟当前员工吃饭耗时
                TimeUnit.SECONDS.sleep(sleep);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
        //等待所有人到齐之后,开车去下一站
        void drive() {
            try {
                long starTime = System.currentTimeMillis();
                //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
                cyclicBarrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),去下一景点的路上!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            //等待所有人到齐之后吃饭,先到的人坐那等着,什么事情不要干
            this.eat();
            //等待所有人到齐之后开车去下一景点,先到的人坐那等着,什么事情不要干
            this.drive();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 10; i++) {
            new T("员工" + i, i).start();
        }
    }
}
// 输出结果
员工10,sleep:10 等待了1(ms),开始吃饭了!
员工1,sleep:1 等待了9004(ms),开始吃饭了!
员工2,sleep:2 等待了8010(ms),开始吃饭了!
员工3,sleep:3 等待了7007(ms),开始吃饭了!
员工4,sleep:4 等待了6008(ms),开始吃饭了!
员工5,sleep:5 等待了5008(ms),开始吃饭了!
员工6,sleep:6 等待了4010(ms),开始吃饭了!
员工7,sleep:7 等待了3010(ms),开始吃饭了!
员工8,sleep:8 等待了2011(ms),开始吃饭了!
员工9,sleep:9 等待了1017(ms),开始吃饭了!
员工10,sleep:10 等待了0(ms),去下一景点的路上!
员工2,sleep:2 等待了7999(ms),去下一景点的路上!
员工1,sleep:1 等待了8999(ms),去下一景点的路上!
员工5,sleep:5 等待了4994(ms),去下一景点的路上!
员工4,sleep:4 等待了5997(ms),去下一景点的路上!
员工3,sleep:3 等待了6997(ms),去下一景点的路上!
员工9,sleep:9 等待了990(ms),去下一景点的路上!
员工8,sleep:8 等待了1993(ms),去下一景点的路上!
员工7,sleep:7 等待了2992(ms),去下一景点的路上!
员工6,sleep:6 等待了3995(ms),去下一景点的路上!

例子3,最后一个任务完成特殊内容

员工10是最后到达的,让所有人都久等了,那怎么办?得给所有人倒酒,然后开饭。

public class CyclicBarrierDemo3 {
    public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
        //模拟倒酒,花了2秒,又得让其他9个人等2秒
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "说,不好意思,让大家久等了,给大家倒酒赔罪!");
    });
    public static class T extends Thread {
        int sleep;
        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }
        @Override
        public void run() {
            try {
                //休眠,即模拟先后到达餐桌的时间
                TimeUnit.SECONDS.sleep(sleep);
                long starTime = System.currentTimeMillis();
                //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
                cyclicBarrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 10; i++) {
            new T("员工" + i, i).start();
        }
    }
}
// 输出结果
员工10说,不好意思,让大家久等了,给大家倒酒赔罪!
员工10,sleep:10 等待了2003(ms),开始吃饭了!
员工1,sleep:1 等待了11007(ms),开始吃饭了!
员工3,sleep:3 等待了9007(ms),开始吃饭了!
员工5,sleep:5 等待了7007(ms),开始吃饭了!
员工2,sleep:2 等待了10008(ms),开始吃饭了!
员工6,sleep:6 等待了6003(ms),开始吃饭了!
员工8,sleep:8 等待了4007(ms),开始吃饭了!
员工4,sleep:4 等待了8005(ms),开始吃饭了!
员工9,sleep:9 等待了3008(ms),开始吃饭了!
员工7,sleep:7 等待了5004(ms),开始吃饭了!

代码中创建CyclicBarrier对象时,多传入了一个参数(内部是倒酒操作),先到的人先等待,待所有人都到齐之后,需要先给大家倒酒,然后唤醒所有等待中的人让大家开饭。从输出结果中我们发现,倒酒操作是由最后一个人操作的,最后一个人倒酒完毕之后,才唤醒所有等待中的其他员工,让大家开饭。

例子4,等待中有一个任务被中断了

员工5等待中,突然接了个电话,有点急事,然后就拿起筷子开吃了,其他人会怎么样呢,看着他吃么?

public class CyclicBarrierDemo4 {
    public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
    public static class T extends Thread {
        int sleep;
        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }
        @Override
        public void run() {
            long starTime = 0, endTime = 0;
            try {
                //休眠,即模拟先后到达餐桌的时间
                TimeUnit.SECONDS.sleep(sleep);
                starTime = System.currentTimeMillis();
                //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
                System.out.println(this.getName() + "到了!");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            endTime = System.currentTimeMillis();
            System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 10; i++) {
            int sleep = 0;
            if (i == 10) {
                sleep = 10;
            }
            T t = new T("员工" + i, sleep);
            t.start();
            if (i == 5) {
                //模拟员工5接了个电话,将自己等待吃饭给打断了
                TimeUnit.SECONDS.sleep(1);
                System.out.println(t.getName() + ",有点急事,我先开干了!");
                t.interrupt();
                TimeUnit.SECONDS.sleep(2);
            }
        }
    }
}
// 输出结果
员工1到了!
员工3到了!
员工4到了!
员工5到了!
员工2到了!
员工5,有点急事,我先开干了!
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
员工5,sleep:0 等待了1007(ms),开始吃饭了!
员工3,sleep:0 等待了1007(ms),开始吃饭了!
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
员工4,sleep:0 等待了1007(ms),开始吃饭了!
员工1,sleep:0 等待了1008(ms),开始吃饭了!
员工2,sleep:0 等待了1007(ms),开始吃饭了!
员工6到了!
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
员工7到了!
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
员工8到了!
java.util.concurrent.BrokenBarrierException
员工6,sleep:0 等待了0(ms),开始吃饭了!
员工9到了!
员工7,sleep:0 等待了1(ms),开始吃饭了!
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
员工9,sleep:0 等待了0(ms),开始吃饭了!
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
员工8,sleep:0 等待了2(ms),开始吃饭了!
员工10到了!
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
	at com.java.thread.demo.cyclicbarrier.CyclicBarrierDemo4$T.run(CyclicBarrierDemo4.java:34)
员工10,sleep:10 等待了0(ms),开始吃饭了!

输出的信息看着有点乱,给大家理一理,员工5遇到急事,拿起筷子就是吃,这样好么?当然不好,他这么做了,后面看他这么做了都跟着这么做(这种场景是不是很熟悉,有一个人拿起筷子先吃起来,其他人都跟着上了),直接不等其他人了,拿起筷子就开吃了。
CyclicBarrier遇到这种情况就是这么处理的。前面4个员工都在await()处等待着,员工5也在await()上等待着,等了1秒(TimeUnit.SECONDS.sleep(1)),接了个电话,然后给员工5发送中断信号后(t.interrupt()),员工5的await()方法会触发InterruptedException异常,此时其他等待中的前4个员工,看着5开吃了,自己立即也不等了,内部从await()方法中触发BrokenBarrierException异常,然后也开吃了,后面的6/7/8/9/10员工来了以后发现大家都开吃了,自己也不等了,6-10员工调用await()直接抛出了BrokenBarrierException异常,然后继续向下。
结论:
1、内部有一个人把规则破坏了(接收到中断信号),其他人都不按规则来了,不会等待了。
2、接收到中断信号的线程,await方法会触发InterruptedException异常,然后被唤醒向下运行。
3、其他等待中或者后面到达的线程,会在await()方法上触发BrokenBarrierException异常,然后继续执行。

什么是Semaphore?

Semaphore底层是基于AbstractQueuedSynchronizer来实现的。Semaphore称为计数信号量,它允许n个任务同时访问某个资源,可以将信号量看做是在向外分发使用资源的许可证,只有成功获取许可证,才能使用资源

例子

张三、李四、王五和赵六4个人一起去饭店吃饭,在特殊时期饭前洗手是必须的,可是饭店只有2个洗手池,洗手池就是不能被同时使用的公共资源,这种场景就可以用到Semaphore。

public class Customer implements Runnable {
    private Semaphore washbasin;
    private String name;
    public Customer(Semaphore washbasin, String name) {
        this.washbasin = washbasin;
        this.name = name;
    }
    @Override
    public void run() {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
            Random random = new Random();
            washbasin.acquire();
            System.out.println(sdf.format(new Date()) + " " + name + " 开始洗手...");
            Thread.sleep((long) (random.nextDouble() * 5000) + 2000);
            System.out.println(sdf.format(new Date()) + " " + name + " 洗手完毕!");
            washbasin.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class SemaphoreTester {
    public static void main(String[] args) throws InterruptedException {
        //饭店里只用两个洗手池,所以初始化许可证的总数为2。
        Semaphore washbasin = new Semaphore(2);

        List<Thread> threads = new ArrayList<>(3);
        threads.add(new Thread(new Customer(washbasin, "张三")));
        threads.add(new Thread(new Customer(washbasin, "李四")));
        threads.add(new Thread(new Customer(washbasin, "王五")));
        threads.add(new Thread(new Customer(washbasin, "赵六")));
        for (Thread thread : threads) {
            thread.start();
            Thread.sleep(50);
        }
        for (Thread thread : threads) {
            thread.join();
        }
    }
}
// 输出结果
06:51:54.416 李四 开始洗手...
06:51:54.416 张三 开始洗手...
06:51:57.251 张三 洗手完毕!
06:51:57.251 王五 开始洗手...
06:51:59.418 李四 洗手完毕!
06:51:59.418 赵六 开始洗手...
06:52:02.496 王五 洗手完毕!
06:52:06.162 赵六 洗手完毕!

Semaphore初始化有10个令牌,11个线程同时各调用1次acquire方法,会发生什么?

拿不到令牌的线程阻塞,不会继续往下运行。

Semaphore初始化有10个令牌,1个线程重复调用11次acquire方法,会发生什么?

线程阻塞,不会继续往下运行。可能你会考虑类似于锁的重入的问题,很好,但是,令牌没有重入的概念。你只要调用一次acquire方法,就需要有一个令牌才能继续运行

Semaphore初始化有1个令牌,1个线程调用一次acquire方法,然后调用两次release方法,之后另外一个线程调用acquire方法,此线程能够获取到足够的令牌并继续运行吗?

能,原因是release方法会添加令牌,并不会以初始化的大小为准。

Semaphore初始化有2个令牌,1个线程调用1次release方法,然后一次性获取3个令牌,会获取到吗?

能,原因是release会添加令牌,并不会以初始化的大小为准。Semaphore中release方法的调用并没有限制要在acquire后调用。代码如下:

public class TestSemaphore2 {
    public static void main(String[] args) {
        int permitsNum = 2;
        final Semaphore semaphore = new Semaphore(permitsNum);
        try {
            System.out.println("availablePermits:" + semaphore.availablePermits() + ", semaphore.tryAcquire(3, 1, TimeUnit.SECONDS):" + semaphore.tryAcquire(3, 1, TimeUnit.SECONDS));
            semaphore.release();
            System.out.println("availablePermits:" + semaphore.availablePermits() + ", semaphore.tryAcquire(3, 1, TimeUnit.SECONDS):" + semaphore.tryAcquire(3, 1, TimeUnit.SECONDS));
        }catch (Exception e) {
        }
    }
}

什么是ThreadLocal?用来解决什么问题的?

1、ThreadLocal是通过线程隔离的方式防止任务在共享资源上产生冲突,线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。
2、ThreadLocal是一个将在多线程中为每一个线程创建单独的变量副本的类,当使用ThreadLocal来维护变量时,ThreadLocal会为每个线程创建单独的变量副本,避免因多线程操作共享变量而导致的数据不一致的情况。

说说你对ThreadLocal的理解

提到ThreadLocal被提到应用最多的是session管理和数据库连接管理,这里以数据访问为例进行理解ThreadLocal:

class ConnectionManager {
    private static Connection connect = null;
    public static Connection openConnection() {
        if (connect == null) {
            connect = DriverManager.getConnection();
        }
        return connect;
    }
    public static void closeConnection() {
        if (connect != null)
            connect.close();
    }
}

很显然,上述代码在多线程中使用会存在线程安全问题:
第一,这里面的2个方法都没有进行同步,很可能在openConnection方法中会多次创建connect;
第二,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。

将Connection设置为非共享的,假如每个线程中都有一个connect变量,各个线程之间对connect变量的访问实际上是没有依赖关系的,即一个线程不需要关心其他线程是否对这个connect进行了修改的。即改后的代码可以这样:

class ConnectionManager {
    private Connection connect = null;
    public Connection openConnection() {
        if (connect == null) {
            connect = DriverManager.getConnection();
        }
        return connect;
    }
    public void closeConnection() {
        if (connect != null)
            connect.close();
    }
}
class Dao {
    public void insert() {
        ConnectionManager connectionManager = new ConnectionManager();
        Connection connection = connectionManager.openConnection();
        // 使用connection进行操作
        connectionManager.closeConnection();
    }
}

上述代码由于每次都是在方法内部创建临时连接,那么自然不存在线程安全问题。但是这样会有一个致命的影响:导致服务器压力非常大,并且严重影响程序执行性能。由于在方法中需要频繁地开启和关闭数据库连接,这样不仅严重影响程序执行效率,还可能导致服务器压力巨大。

这时候ThreadLocal登场:
因为ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。下面就是网上出现最多的例子:

public class ConnectionManager {
    private static final ThreadLocal<Connection> dbConnectionLocal = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            try {
                return DriverManager.getConnection("", "", "");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        }
    };
    public Connection getConnection() {
        return dbConnectionLocal.get();
    }
}

还有哪些使用ThreadLocal的应用场景?

每个线程维护了一个“序列号”:

public class SerialNum {
    // The next serial number to be assigned
    private static int nextSerialNum = 0;
    private static ThreadLocal serialNum = new ThreadLocal() {
        protected synchronized Object initialValue() {
            return new Integer(nextSerialNum++);
        }
    };
    public static int get() {
        return ((Integer) (serialNum.get())).intValue();
    }
}

session管理

private static final ThreadLocal threadSession = new ThreadLocal();  
public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

阿里巴巴 java 开发手册中推荐的 ThreadLocal 的用法:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class DateUtils {
    public static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
}
// 调用的地方
DateUtils.df.get().format(new Date());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值