Java并发编程-多线程基础(五)

ThreadLocal

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。

这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Profiler {
    // 第一次get()方法调用时会进行初始化(如果set方法没有调用),每个线程会调用一次
    private static final ThreadLocal<Long> TIME_THREADLOCAL = ThreadLocal.withInitial(System::currentTimeMillis);
    // SimpleDateFormat 不是线程安全的,所以每个线程都要有自己独立的副本
    private static final ThreadLocal<SimpleDateFormat> formatter =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
    public static void begin() {
        System.out.println("thread " + Thread.currentThread().getName() + " begin at: " + formatter.get().format(new Date()));
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }
    public static long end() {
        System.out.println("thread " + Thread.currentThread().getName() + " end at: " + formatter.get().format(new Date()));
        return System.currentTimeMillis() - TIME_THREADLOCAL.get();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    Profiler.begin();
                    sleep(1);
                    System.out.println("thread " + Thread.currentThread().getName() + " Cost: " + Profiler.end() + " mills");
                }
            }, "" + i);
            t.start();
        }
    }
    public static void sleep(int sec){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

代码里 构建了一个Profiler类(统计线程耗时),它具有begin()和end()两个方法,而end()方法返回: 从begin()方法调用开始到end()方法被调用时的时间差,单位是毫秒。

另外,为了打印格式化的时间,我在这里用到了SimpleDateFormat 类,由于这个类是线程不安全的,这里也把SimpleDateFormat类对象 作为线程独有的了。当然你也可以使用同步锁去保证它的安全,但是其实没必要。

这里给出的这个Profiler例子,其实可以被复用在方法耗时统计的功能上,在方法的入口前执行begin()方法,在 方法调用后执行end()方法,好处是两个方法的调用不用在一个方法或者类中,比如在AOP(面向方面编程)中,可以在方法调用前的切入点执行begin()方法,而在方法调用后的切入点执行 end()方法,这样依旧可以获得方法的执行耗时。

你可能感兴趣的:(Java基础系列,java,jvm,开发语言)