《多线程编程》学习之十:定时器Timer的使用,线程安全的单例模式
2016-11-08 16:21
507 查看
一、定时器Timer的使用
定时器 Timer类主要的作用是设置计划任务,它在内部使用多线程的方式进行处理;而抽象的TimerTask类负责封装任务,它实现了Runnable接口。
1)schedule(TimerTask task,Date time)
此方法表示,如果设定的时间早于当前时间,则立即执行;否则等待time指定的时间再执行task任务。
指定时间:2016-11-08 16:26:00 当前时间为:2016-11-08 16:25:25
开始执行任务,时间为:2016-11-08 16:26:00
结束执行任务,时间为:2016-11-08 16:26:03
2)schedule(TimerTask task, Date firstDate, long period)
此方法作用是在指定日期之后,按指定的时间间隔无限循环执行任务。如果任务的执行时间大于period,则等此次任务执行完毕后立即循环执行下次任务。在上面例子的基础上,测试代码如下:
3)多个TimeTask任务及延时
Timer中允许多个TimerTask任务,它们是以队列的方式一个一个地被顺序执行的,如果前面的任务执行时间过长,则下一个任务执行的时间可能与预期的时间不一致。
二、线程安全的单例模式
1)立即加载(“饿汉模式”)
所谓立即加载,就是使用类的时候已经将对象创建完毕了,常见的是直接new实例化对象。
2)延迟加载(“懒汉模式”)
所谓延迟加载,就是在调用getInstance()方法时实例才被创建。
运行结果:
可见程序创建了三个对象,并不是单例模式。下面介绍线程安全的延迟加载的单例模式。
3)使用双重检锁机制
可以直接对getInstance()方法声明synchronized关键字来达到线程安全,但这种方法运行效率非常低下,下一个线程需要得到对象,则必须等待上一次线程释放锁。可以使用双重检锁机制来解决这一问题。
DoubleLockThread.java的代码如下:
/**
* 访问单例(双重检锁机制下)的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class DoubleLockThread extends Thread {
@Override
public void run() {
System.out.println(DoubleLockSingleton.getInstance().hashCode());
}
}
运行结果:
4)使用静态内部类实现线程安全的单例模式
/**
* 使用静态内部类实现线程安全的单例模式
* Created by xiuzhen on 2016/11/8.
*/
public class InnerSingleton {
//私有的构造函数
private InnerSingleton() {
}
//静态内部类创建单例
private static class SingletonHandler {
private static InnerSingleton singleton = new InnerSingleton();
}
//外部接口(外部类可以访问内部类的私有成员)
public static InnerSingleton getInstance() {
return SingletonHandler.singleton;
}
//测试
public static void main(String[] args) {
staticInnerThread thread1 = new staticInnerThread();
staticInnerThread thread2 = new staticInnerThread();
staticInnerThread thread3 = new staticInnerThread();
thread1.start();
thread2.start();
thread3.start();
}
}staticInnerThread.java的代码如下:
/**
* 访问静态内部类实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class staticInnerThread extends Thread {
@Override
public void run() {
System.out.println(InnerSingleton.getInstance().hashCode());
}
}运行结果:
5)使用static代码块实现单例模式
静态代码块中的代码在使用类时就已经执行了,可以利用此特性来实现单例。
/**
* 利用静态代码块实现单例模式(静态代码块在使用类之前已经存在了)
* Created by xiuzhen on 2016/11/8.
*/
public class StaticBlockSingleton {
//私有构造函数
private StaticBlockSingleton() {}
//单例变量
private static StaticBlockSingleton singleton;
//静态代码块
static {
singleton = new StaticBlockSingleton();
}
//对外提供访问单例的接口
public static StaticBlockSingleton getInstance() {
return singleton;
}
//测试
public static void main(String[] args) {
StaticBlockThread thread1 = new StaticBlockThread();
StaticBlockThread thread2 = new StaticBlockThread();
StaticBlockThread thread3 = new StaticBlockThread();
thread1.start();
thread2.start();
thread3.start();
}
}StaicBlockThread.java的代码如下:
/**
* 访问静态代码块实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class StaticBlockThread extends Thread {
@Override
public void run() {
System.out.println(StaticBlockSingleton.getInstance().hashCode());
}
}运行结果:
6)使用enum枚举实现单例模式
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* enum像特殊的类,有构造器,方法和数据域。枚举类的构造器只能是私有的,
* 在构造枚举值时被调用。枚举值都是public static final的。
* Created by xiuzhen on 2016/11/8.
*/
public class EnumSingleton {
//利用枚举类型得到唯一的Connection实例
private enum MyEunmSingleton {
//枚举值
CONNECTION_FACTORY;
//私有成员变量
private Connection connection;
//私有构造器
private MyEunmSingleton() {
try {
System.out.println("创建Connection对象");
String url = "jdbc:mysql://localhost:3306/ali_blog";
String username = "root";
String password = "";
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接(实例化Connection)
connection = DriverManager.getConnection(url, username, password);
} catch(ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
}
}
//获取Connection实例
private Connection getConnection() {
return connection;
}
}
//对外提供访问Connection的单例(单一职责原则,不将枚举类对外暴露)
public static Connection getConnection() {
//枚举类.枚举值.方法
return MyEunmSingleton.CONNECTION_FACTORY.getConnection();
}
//测试
public static void main(String[] args) {
EnumThread thread1 = new EnumThread();
thread1.start();
}
}EnumThread.java的代码:
/**
* 访问枚举类实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class EnumThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println(EnumSingleton.getConnection().hashCode());
}
}
}运行结果:
定时器 Timer类主要的作用是设置计划任务,它在内部使用多线程的方式进行处理;而抽象的TimerTask类负责封装任务,它实现了Runnable接口。
1)schedule(TimerTask task,Date time)
此方法表示,如果设定的时间早于当前时间,则立即执行;否则等待time指定的时间再执行task任务。
<span style="font-size:18px;">import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; /** * Timer类主要用于设置计划任务,TimerTask类用于封装任务 * 例子一:在指定的时间执行任务。如果指定时间比现在早,则立即执行;如果指定的时间比现在晚,则将来执行。 * Created by xiuzhen on 2016/11/8. */ public class Run1 { //创建一个Timer,默认为非守护线程(添加参数true,则创建守护线程,TimerTask中的任务不再被运行) static private Timer timer = new Timer(); //日期格式 static private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //创建抽象TimerTask类的子类 static class MyTask extends TimerTask { @Override public void run() { try { //打印执行的当前时间 System.out.println("开始执行任务,时间为:" + sdf.format(new Date())); Thread.sleep(3000); //休眠3s System.out.println("结束执行任务,时间为:" + sdf.format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } //测试:在指定的时间执行任务 public static void main(String[] args) { try { //创建任务 MyTask task = new MyTask(); //任务执行的时间 String dateString = "2016-11-08 16:26:00"; Date date = sdf.parse(dateString); System.out.println("指定时间:" + sdf.format(date) + " 当前时间为:" + sdf.format(new Date())); //定时器设置任务,在指定时间执行任务 timer.schedule(task, date); } catch (ParseException e) { e.printStackTrace(); } } }</span>运行结果:
指定时间:2016-11-08 16:26:00 当前时间为:2016-11-08 16:25:25
开始执行任务,时间为:2016-11-08 16:26:00
结束执行任务,时间为:2016-11-08 16:26:03
2)schedule(TimerTask task, Date firstDate, long period)
此方法作用是在指定日期之后,按指定的时间间隔无限循环执行任务。如果任务的执行时间大于period,则等此次任务执行完毕后立即循环执行下次任务。在上面例子的基础上,测试代码如下:
<span style="font-size:18px;"> public static void main(String[] args) { try { //创建任务 MyTask task = new MyTask(); //任务执行的时间 String dateString = "2016-11-08 16:32:00"; Date date = sdf.parse(dateString); System.out.println("指定时间:" + sdf.format(date) + " 当前时间为:" + sdf.format(new Date())); //在指定日期之后,按指定的时间间隔无限循环地执行某一任务,如果任务执行的时间过长,则等任务执行完后立即循环执行。 timer.schedule(task, date, 2000); } catch (ParseException e) { e.printStackTrace(); } }</span>测试结果:
3)多个TimeTask任务及延时
Timer中允许多个TimerTask任务,它们是以队列的方式一个一个地被顺序执行的,如果前面的任务执行时间过长,则下一个任务执行的时间可能与预期的时间不一致。
<span style="font-size:14px;">import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; /** * 多个TimerTask任务及延时的测试。 * TimerTask是以队列的方式一个一个被顺序执行的,如果前面的任务执行的时间太长,可能导致后面的任务延时执行。 * Created by xiuzhen on 2016/11/8. */ public class Run2 { static private Timer timer = new Timer(); static private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //第一个任务类 static private class MyTask1 extends TimerTask { @Override public void run() { try { System.out.println("MyTask1开始运行,时间为:" + sdf.format(new Date())); Thread.sleep(5000); //休眠5s System.out.println("MyTask1结束运行,时间为:" + sdf.format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } //第二个任务类 static private class MyTask2 extends TimerTask { @Override public void run() { System.out.println("MyTask2运行了,时间为:" + sdf.format(new Date())); } } //测试:多个TimerTask任务运行,可能会延时 public static void main(String[] args) { try { //任务一: MyTask1 task1 = new MyTask1(); String dateString1 = "2016-11-8 16:40:00"; Date date1 = sdf.parse(dateString1); System.out.println("任务一指定的时间为:" + sdf.format(date1) + " 当前时间为:" + sdf.format(new Date())); //任务二: MyTask2 task2 = new MyTask2(); String dateString2 = "2016-11-8 16:40:01"; Date date2 = sdf.parse(dateString2); System.out.println("任务二指定的时间为:" + sdf.format(date2) + " 当前时间为:" + sdf.format(new Date())); timer.schedule(task1, date1); timer.schedule(task2, date2); } catch (ParseException e) { e.printStackTrace(); } } }</span>运行结果:
二、线程安全的单例模式
1)立即加载(“饿汉模式”)
所谓立即加载,就是使用类的时候已经将对象创建完毕了,常见的是直接new实例化对象。
<span style="font-size:18px;"><pre style="font-family: Consolas; font-size: 13.5pt; background-color: rgb(255, 255, 255);"><pre name="code" class="java">/** * 饿汉模式(静态实例变量) * Created by xiuzhen on 2016/11/8. */ public class HungerSingleton { //立即加载(饿汉模式):静态实例变量 private static HungerSingleton singleton = new HungerSingleton(); //私有的构造函数 private HungerSingleton () { } //对外提供访问单例的接口 public static HungerSingleton getInstance() { return singleton; } //测试:创建三个线程,去访问单例 public static void main(String[] args) { HungerThread thread1 = new HungerThread(); HungerThread thread2 = new HungerThread(); HungerThread thread3 = new HungerThread(); thread1.start(); thread2.start(); thread3.start(); } }</span>
HungerThread.java的代码如下:
/** * 访问单例(饿汉模式下)的线程类 * Created by xiuzhen on 2016/11/8. */ public class HungerThread extends Thread { @Override public void run() { System.out.println(HungerSingleton.getInstance().hashCode()); } }运行结果:
2)延迟加载(“懒汉模式”)
所谓延迟加载,就是在调用getInstance()方法时实例才被创建。
<span style="font-size:18px;">/** * 延迟加载(饿汉模式) * Created by xiuzhen on 2016/11/8. */ public class LazySingleton { private static LazySingleton singleton; //私有构造函数 private LazySingleton() { } //延迟加载,调用方法是才去实例化对象 public static LazySingleton getInstance() { try { if (singleton == null) { //模拟在创建对象前需要做一些准备工作 Thread.sleep(3000); singleton = new LazySingleton(); } } catch (InterruptedException e) { e.printStackTrace(); } return singleton; } //测试,这种延迟加载,可能会出现线程安全问题 public static void main(String[] args) { LazyThread thread1 = new LazyThread(); LazyThread thread2 = new LazyThread(); LazyThread thread3 = new LazyThread(); thread1.start(); thread2.start(); thread3.start(); } }</span>
LazyThread.java的代码如下:
/** * 访问单例(懒汉模式下)的线程类 * Created by xiuzhen on 2016/11/8. */ public class LazyThread extends Thread { @Override public void run() { System.out.println(LazySingleton.getInstance().hashCode()); } }
运行结果:
可见程序创建了三个对象,并不是单例模式。下面介绍线程安全的延迟加载的单例模式。
3)使用双重检锁机制
可以直接对getInstance()方法声明synchronized关键字来达到线程安全,但这种方法运行效率非常低下,下一个线程需要得到对象,则必须等待上一次线程释放锁。可以使用双重检锁机制来解决这一问题。
<span style="font-size:18px;">/** * 双重检锁机制,实现线程安全的延迟加载单例模式 * Created by xiuzhen on 2016/11/8. */ public class DoubleLockSingleton { private static DoubleLockSingleton singleton; private DoubleLockSingleton() {} public static DoubleLockSingleton getInstance() { try { if (singleton == null) { //模拟在创建对象前需要做一些准备工作 Thread.sleep(3000); synchronized (DoubleLockSingleton.class) { if(singleton == null) { //这里进行第二次判断,不能省略,否则会线程不安全。 singleton = new DoubleLockSingleton(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return singleton; } public static void main(String[] args) { DoubleLockThread thread1 = new DoubleLockThread(); DoubleLockThread thread2 = new DoubleLockThread(); DoubleLockThread thread3 = new DoubleLockThread(); thread1.start(); thread2.start(); thread3.start(); } }</span>
DoubleLockThread.java的代码如下:
/**
* 访问单例(双重检锁机制下)的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class DoubleLockThread extends Thread {
@Override
public void run() {
System.out.println(DoubleLockSingleton.getInstance().hashCode());
}
}
运行结果:
4)使用静态内部类实现线程安全的单例模式
/**
* 使用静态内部类实现线程安全的单例模式
* Created by xiuzhen on 2016/11/8.
*/
public class InnerSingleton {
//私有的构造函数
private InnerSingleton() {
}
//静态内部类创建单例
private static class SingletonHandler {
private static InnerSingleton singleton = new InnerSingleton();
}
//外部接口(外部类可以访问内部类的私有成员)
public static InnerSingleton getInstance() {
return SingletonHandler.singleton;
}
//测试
public static void main(String[] args) {
staticInnerThread thread1 = new staticInnerThread();
staticInnerThread thread2 = new staticInnerThread();
staticInnerThread thread3 = new staticInnerThread();
thread1.start();
thread2.start();
thread3.start();
}
}staticInnerThread.java的代码如下:
/**
* 访问静态内部类实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class staticInnerThread extends Thread {
@Override
public void run() {
System.out.println(InnerSingleton.getInstance().hashCode());
}
}运行结果:
5)使用static代码块实现单例模式
静态代码块中的代码在使用类时就已经执行了,可以利用此特性来实现单例。
/**
* 利用静态代码块实现单例模式(静态代码块在使用类之前已经存在了)
* Created by xiuzhen on 2016/11/8.
*/
public class StaticBlockSingleton {
//私有构造函数
private StaticBlockSingleton() {}
//单例变量
private static StaticBlockSingleton singleton;
//静态代码块
static {
singleton = new StaticBlockSingleton();
}
//对外提供访问单例的接口
public static StaticBlockSingleton getInstance() {
return singleton;
}
//测试
public static void main(String[] args) {
StaticBlockThread thread1 = new StaticBlockThread();
StaticBlockThread thread2 = new StaticBlockThread();
StaticBlockThread thread3 = new StaticBlockThread();
thread1.start();
thread2.start();
thread3.start();
}
}StaicBlockThread.java的代码如下:
/**
* 访问静态代码块实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class StaticBlockThread extends Thread {
@Override
public void run() {
System.out.println(StaticBlockSingleton.getInstance().hashCode());
}
}运行结果:
6)使用enum枚举实现单例模式
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* enum像特殊的类,有构造器,方法和数据域。枚举类的构造器只能是私有的,
* 在构造枚举值时被调用。枚举值都是public static final的。
* Created by xiuzhen on 2016/11/8.
*/
public class EnumSingleton {
//利用枚举类型得到唯一的Connection实例
private enum MyEunmSingleton {
//枚举值
CONNECTION_FACTORY;
//私有成员变量
private Connection connection;
//私有构造器
private MyEunmSingleton() {
try {
System.out.println("创建Connection对象");
String url = "jdbc:mysql://localhost:3306/ali_blog";
String username = "root";
String password = "";
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接(实例化Connection)
connection = DriverManager.getConnection(url, username, password);
} catch(ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
}
}
//获取Connection实例
private Connection getConnection() {
return connection;
}
}
//对外提供访问Connection的单例(单一职责原则,不将枚举类对外暴露)
public static Connection getConnection() {
//枚举类.枚举值.方法
return MyEunmSingleton.CONNECTION_FACTORY.getConnection();
}
//测试
public static void main(String[] args) {
EnumThread thread1 = new EnumThread();
thread1.start();
}
}EnumThread.java的代码:
/**
* 访问枚举类实现的单例的线程类
* Created by xiuzhen on 2016/11/8.
*/
public class EnumThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println(EnumSingleton.getConnection().hashCode());
}
}
}运行结果:
相关文章推荐
- Android学习指南之三十四:Android定时器Timer的使用
- Java学习之道: java定时器的使用(Timer)
- iOS 单例模式 学习 "52个方法 第6章 45条 使用 dispath_once 来执行只需运行一次的线程安全代码"
- 《多线程编程》学习之一:使用多线程及线程安全
- QT 定时器 timer 使用
- 学习心得 Tianmao 篇 RecyclerView.Adapter 我自己的使用理解(类组合模式)
- Material Design 风格 结合使用 Retrofit RxJava Jsoup Mvp 模式的一款资讯类 学习 app
- java定时器的使用(Timer)
- 《多线程编程》学习之八:方法join()的使用、类ThreadLocal的使用
- 《多线程编程》学习之九:Lock的使用
- 张孝祥[致敬]-多线程学习第02课 传统定时器的使用
- 定时器使用_Timer
- Linux学习总结(18)——Linux使用init命令关机、重启、切换模式
- Linux学习总结(18)——Linux使用init命令关机、重启、切换模式
- Java基础学习总结(85)——Java中四种线程安全的单例模式实现方式
- Java基础学习总结(85)——Java中四种线程安全的单例模式实现方式
- C#中Timer定时器的使用示例
- vmware在桥接模式下配置centos7网络,并使用xshell连接虚拟主机(学习过程篇)
- Nordic定时器配置(Timer模式)
- Java 定时器(Timer)及线程池里使用定时器实例代码