2020-02-02 3.1 线程封闭

本文是Java线程安全和并发编程知识总结的一部分。

3.1 线程封闭

线程封闭的意思是:

只在线程内共享数据,不在线程之间共享数据,从而避开线程间数据共享的需求。

实现线程封闭的方式大致有三种,下面详细描述。

3.1.1. Ad-hoc线程封闭。

即通过良好的设计,避免共享数据。

比如,使用volatile修饰状态属性,并确保只在单线程中修改状态属性,或并不修改状态属性。
关于 volatile 关键词的作用,在1. Java语言提供的基本线程安全保护的1.3章节中,有详细描述;并在 2.1 原子量工具包中有举例说明其优点和缺点;本章不再详细描述。

3.1.2. 栈封闭。

只通过局部变量共享数据;而局部数据由栈保护,天生是线程安全的。

栈封闭在1. Java语言提供的基本线程安全保护一章有详细描述,就不再重复了。

3.1.3. 使用ThreadLocal共享数据。

ThreadLocal 是JDK的一个内置工具类,相当于一个线程安全的全局共享Map,但以当前线程为key,以变量为value,从而将变量绑定到当前线程上。

需要注意的是:

  • ThreadLocal的效果是让每个线程都有自己的副本,相当于一个线程内的全局变量,只能实现线程内的数据共享,不能实现线程之间的数据共享。
  • 既然类似全局变量,就具有全局变量的缺点,需要防止被滥用。
  • 如果当前线程被复用,那么就要小心第一次任务在ThreadLocal中设置的变量,在第二个任务复用该线程时被使用。特别是在线程池中。

比如:

/**
 * @author xx
 * 2020年1月31日 下午4:08:45
 */
public class Sample10 {
    
    /**
     * 
     * 2020年2月2日 下午12:05:10 xx添加此方法
     * @param args
     */
    public static void main(String[] args) {
        Sample10 sample10 = new Sample10();
        for (int i = 0; i < 5; i++) {
            new Thread(() ->  {
                sample10.startCalc();
            }).start();
        }
    }
    
    /**
     * 表示某个方法的累计执行次数
     */
    private static ThreadLocal threadStartTime = new ThreadLocal<>() {
        @Override
        protected Instant initialValue() {
            return Instant.now();
        }
    };
    
    /**
     * 使用ThreadLocal保存了当前线程的启动时间
     * 2020年2月2日 下午12:04:32 xx添加此方法
     */
    public void startCalc() {
        // 模拟做业务操作。
        int randonMs = new Random(5).nextInt(1000);
        try {
            Thread.sleep(randonMs);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        long aliveMs = Instant.now().getEpochSecond() - Sample10.threadStartTime.get().getEpochSecond();
        System.out.println("当前线程已运行了 " + aliveMs + " 秒。");
    }
}

你可能感兴趣的:(2020-02-02 3.1 线程封闭)