Java-时间单例线程

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-时间单例线程)