美文网首页Java奇巧淫技
Java-时间单例线程

Java-时间单例线程

作者: Chermack | 来源:发表于2020-10-06 18:00 被阅读0次

    System.currentTimeMillis()在Java中是一个native方法,用于获取当前毫秒时间戳,虽然不用担心线程安全问题,但是在超高并发的调用时会产生性能瓶颈。通常在记录操作日志为了获取时间都会去调用该方法,如果并发数量过大,会发现累积起来的性能损耗十分严重,为了减少调用该方法的次数,可以使用时间单例解决。

    目前网络上流行的一种作法如下:

    class SystemClock {
        private static final String THREAD_NAME = "system.clock";
        private static final SystemClock MILLIS_CLOCK = new SystemClock(1);
        private final long precision;
        private final AtomicLong now;
    
        private SystemClock(long precision) {
            this.precision = precision;
            now = new AtomicLong(System.currentTimeMillis());
            scheduleClockUpdating();
        }
    
        public static SystemClock millisClock() {
            return MILLIS_CLOCK;
        }
    
        private void scheduleClockUpdating() {
            ScheduledExecutorService scheduler =
                    Executors.newSingleThreadScheduledExecutor(runnable -> {
                        Thread thread = new Thread(runnable, THREAD_NAME);
                        thread.setDaemon(true);
                        thread.setPriority(Thread.MAX_PRIORITY); //设置线程优先级,让时间线程尽可能运行(依赖于操作系统的调度实现)
                        return thread;
                    });
            scheduler.scheduleAtFixedRate(() ->
                    now.set(System.currentTimeMillis()), precision, precision, TimeUnit.MILLISECONDS);
        }
    
        public long now() {
            return now.get();
        }
    }
    

    其实完全不需要依赖定时调度器ScheduledExecutorService 和线程安全的AtomicLong类,一个简化的版本如下:

    class SystemClock2 {
        private static final String THREAD_NAME = "system.clock2";
        private static final long TIME_TICK = 1;
        private static final SystemClock2 instance = new SystemClock2();
        private volatile long now = System.currentTimeMillis();
    
        public SystemClock2() {
            Thread timerThread = new Thread(() -> {
                while (true) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(TIME_TICK);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    now = System.currentTimeMillis();
                }
            }, THREAD_NAME);
            timerThread.setDaemon(true);
            timerThread.setPriority(Thread.MAX_PRIORITY); //设置线程优先级,让时间线程尽可能运行(依赖于操作系统的调度实现)
            timerThread.start();
        }
    
        public static SystemClock2 getInstance() {
            return instance;
        }
    
        public long now() {
            return now;
        }
    }
    

    注:

    • 上述两种实现方式都是按照1毫秒更新一次时间,也可自行按需设定。
    • 上述两种单例模式都是“饿汉式”而不是“懒汉式”,因为该类被调用只可能用于获取时间,因此第一次类初始化后就可以直接启动时间更新线程了,同时可以减少“懒汉式”单例双检锁(double-check lock)的判空步骤,性能更高。

    性能测试程序如下:

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class TimeSingleton {
        public static void main(String[] args) {
            int times = Integer.MAX_VALUE;
    
            new Thread(() -> {
                long start = System.currentTimeMillis();
                for (long j = 0; j < times; j++) {
                    SystemClock.millisClock().now();
                }
                long end = System.currentTimeMillis();
                System.out.println("SystemClock Time:" + (end - start) + "毫秒");
            }).start();
    
            new Thread(() -> {
                long start = System.currentTimeMillis();
                for (long j = 0; j < times; j++) {
                    SystemClock2.getInstance().now();
                }
                long end = System.currentTimeMillis();
                System.out.println("SystemClock2 Time:" + (end - start) + "毫秒");
            }).start();
    
            new Thread(() -> {
                long start = System.currentTimeMillis();
                for (long j = 0; j < times; j++) {
                    System.currentTimeMillis();
                }
                long end = System.currentTimeMillis();
                System.out.println("SystemCurrentTimeMillis Time:" + (end - start) + "毫秒");
            }).start();
    
        }
    }
    

    在AMD 1700X,windows10 2004系统下测试结果:


    对比实验.png

    相关文章

      网友评论

        本文标题:Java-时间单例线程

        本文链接:https://www.haomeiwen.com/subject/fovtpktx.html