并发编程定时线程池&DCL
大家里面的案例可以从gitHub下载下来自己看一下
地址:https://github.com/JolyouLu/Juc-study.git 代码在Juc-ScheduledExecutor下
定时线程池简介
之前我们讲的ThreadPoolExecutor是java的普通线程池。而ScheduledThreadPoolExecutor是java提供的定时任务线程池指定在某一个时间段运行指定代码。
常用方法
java.util.concurrent.ScheduledThreadPoolExecutor#schedule 延时任务java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate 固定速率连续执行java.util.concurrent.ScheduledThreadPoolExecutor#scheduleWithFixedDelay非固定速率连续执行java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue延迟队列
schedule
schedule方法传入3个参数 Runnable(线程),delay(需要延时时间),TimeUnit(时间单位),让线程延时到指定时间后运行
public class ScheduledThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.schedule(()->{
System.out.println("延时任务:10秒后运行一次");
},10, TimeUnit.SECONDS);
scheduledExecutorService.schedule(()->{
System.out.println("延时任务:3秒后运行一次");
},3, TimeUnit.SECONDS);
}
}
scheduleAtFixedRate
scheduleAtFixedRate 方法传入4个参数Runnable(线程),initialDelay(第一次运行需要延时的时间),delay(重复运行相距时间),TimeUnit(时间单位),让线程延时到指定时间后一直循环重复运行(不管你业务有没有执行完到达指定时间后执行任务)
public class ScheduledThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleAtFixedRate(()->{
System.out.println("定时任务:3秒跑一次");
},0,2,TimeUnit.SECONDS);
}
}
单列模式(Double check lock)
单列模式意思就是这个程序中永远都是只有这个对象,不会再new一个对象出来,但是在并发的情况下就可能会创建多个对象这个是我们不想的,以下有4种方法可以避免这类事情的发生
实现1
饿汉模式,字面意思理解,就是这段代码很饿,程序一初始化时候已经创建了这个对象,有用程序初始时就要创建对象会引起不必要的资源消耗。
//饿汉模式 线程安全 消耗资源
public class Singleton1 {
private static Singleton1 singleton1 = new Singleton1();
private Singleton1(){
}
public static Singleton1 getSingleton1(){
return singleton1;
}
public static void main(String[] args) {
for (int i = 0;i<100;i++){
new Thread(()->{
System.out.println(getSingleton1().hashCode());
}).start();
}
}
}
实现2
懒汉模式,字面意思理解,就是这段代码很懒,等有人调用他的时候再初始化,由于创建对象时如果在并发状态下的不能保持单例,所以在创建对象前必须判断和加锁保证线程安全。
//懒汉模式 线程安全 使用双重检测
public class Singleton2 {
private static volatile Singleton2 singleton2 = null;
private Singleton2(){
}
public static Singleton2 getSingleton2(){
if (null == singleton2){
synchronized (Singleton2.class){
if (null == singleton2){
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
public static void main(String[] args) throws InterruptedException {
final Set set;
set = new CopyOnWriteArraySet();
TlUtil.timeTasks(100, 1, new Runnable() {
@Override
public void run() {
set.add(getSingleton2().hashCode());
}
});
System.out.println(set.size());
}
}
实现3
使用枚举类创建,因为枚举在jvm中只会被调用一次,所以可以确保线程安全,也是现在最推荐的用法。
//使用枚举方式 确保线程安全
public class Singleton3 {
public Singleton3() {
}
private enum SingletonEnum{
SINGLETON_ENUM;
private Singleton3 singleton3;
//jvm保证这个方法绝对的调用一次
SingletonEnum(){singleton3 = new Singleton3();}
public Singleton3 getSingleton3(){return singleton3;}
}
public static Singleton3 getSingleton3(){
return SingletonEnum.SINGLETON_ENUM.getSingleton3();
}
public static void main(String[] args) {
final Set set;
set = new CopyOnWriteArraySet();
for (int i = 0;i<100;i++){
new Thread(()->{
set.add(getSingleton3().hashCode());
}).start();
}
System.out.println(set.size());
}
}
实现4
//静态内部类实现线程安全的单例
public class Singleton4 {
private Singleton4(){
}
private static class SingletonInner{
private static final Singleton4 SINGLETON_4 = new Singleton4();
}
public static Singleton4 getSingleton4(){
return SingletonInner.SINGLETON_4;
}
public static void main(String[] args) {
final Set set;
set = new CopyOnWriteArraySet();
for (int i = 0;i<100;i++){
new Thread(()->{
set.add(getSingleton4().hashCode());
}).start();
}
System.out.println(set.size());
}
}