Java-时间单例线程
2020-10-06 本文已影响0人
Chermack
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