美文网首页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-时间单例线程

    System.currentTimeMillis()在Java中是一个native方法,用于获取当前毫秒时间戳,虽...

  • JAVA-线程-一-执行器Executor

    2. JAVA-线程-二-ExecutorService 接口 3. JAVA-线程-三-AbstractExec...

  • swift单例

    普通版单例 多线程版单例

  • 单例模式(Singleton)

    一、初始化单例类时即创建单例 饿汉式:(线程安全) 枚举类型:(线程安全) 二、按需、延迟创建单例 懒汉式:(线程...

  • JAVA-线程-三-AbstractExecutorServic

    1. JAVA-线程-一-执行器Executor 2. JAVA-线程-二-ExecutorService 接口 ...

  • JAVA-线程-二-ExecutorService 接口

    1. JAVA-线程-一-执行器Executor 3. JAVA-线程-三-AbstractExecutorSer...

  • Java-单例

    单例模式的作用? 单例模式主要保证在Java程序中,一个类class只有一个实例存在。能够节约内存,方便管理,更好...

  • 设计模式——单例模式的破坏

    概述: 之前学习了单例模式的几种实现,解决了多线程情况下,单例的线程安全问题,保证了单例的实现。但是单例模式在下面...

  • OC 和 swift 创建单例方法

    OC单例 1.使用GCD(推荐使用) 2.单线程模式单例 此单例只有在单个线程使用的情况下实用,在多线程的情况下,...

  • 设计模式(2) 单例模式

    单例模式 线程安全的Singleton 会破坏Singleton的情况 线程级Singleton 单例模式是几个创...

网友评论

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

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