定时器与单例模式
本次更新的主要内容是多线程中的定时器Timer和单例模式,本来打算分两次更新的,这次就在一起更新了。这次更新完之后,多线程的基础理论知识系列就完结了,后续会发布Mysql的内部技术分析,多线程实战等内容,也会不定时的在每一期穿插更新一个算法或者编程技巧。
主要内容如下:
1 、定时器
在JDK中,Timer类主要负责计划任务的功能,也就是在执行定的时间开始执行某一个任务,Timer类的主要任务是设置计划任务,但封装任务的类却是TimerTask类(抽象类)。
1.1、方法schedule(TimerTask task,Date time)
该方法的作用是在指定的日期执行一次某一个任务。
样例如下:
public class MyTask extends TimerTask {
@Override
public void run() {
//执行代码
}
}
MyTask task = new MyTask();
Timer timer = new Timer();
timer.schedule(task,new Date());
timer线程启动后,会一直存在,并没有结束,不能设置为守护线程,会影响task的执行。
- 执行时间晚于当前时间。
到时间自动执行。 - 执行时间早于当前时间。
立即执行 - Timer中有多个TimerTask任务及任务延时。
TimerTask是以队列的方式一个一个被顺序地执行,所以执行时间有可能和预期不一样,因为前面的任务有可能消耗过大,后面的任务的运行时间也被延后。被延后的任务,会在前一个任务结束后立即执行。
1.2、方法schedule(TimerTask task,Date first,long period)
该方法的作用是在指定的日期之后按指定的时间间隔,无线循环地执行某一任务。
- 执行时间晚于当前时间。
到时间自动执行。 - 执行时间早于当前时间。
立即执行 - 执行任务时间被延时。
任务的消耗时间大于period值,任务被无间隔的循环执行。 -
TimerTask类的cancel()方法。
该方法的作用是将自身从任务队列中进行删除。其他任务不受影响。 - Timer类的cancel方法。
该方法的作用是将任务队列中的全部任务进行清除。且进程被摧毁。该方法并不一定总是生效的,失败的原因是没有争抢到queue锁。
1.3、方法schedule(TimerTask task,long delay)
该方法是以当前时间为参考,再次基础上延迟一定的毫秒数后执行一次TimerTask任务。
1.4、方法schedule(TimerTask task,long delay,long period)
该方法是以当前时间为参考,在此时间基础上,延迟指定的毫秒数,再以某一时间间隔无限次的执行某一任务。
1.5、方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
方法schedule和scheduleAtFixedRate都会按顺序执行,所以不要考虑非线程安全的情况。主要区别在于有没有追赶性。
scheduleAtFixedRate方法具有追赶性,执行任务时间晚于计划时间,被卡掉的时间的任务,也会被执行。而schedule方法不会。
2、单例模式
2.1、饿汉模式
饿汉模式,在调用方法前,实例已经被创建了。
public class MyObject {
//立即加载方式
private static MyObject myObject = new MyObject();
private MyObject(){
}
public static MyObject getInstance() {
//立即加载
//缺点不能有其他实例
//get方法没有同步
return myObject;
}
}
2.2、懒汉模式
懒汉模式,在调用方法时才被创建。
public class MyObject {
//立即加载方式
private static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance() {
if(myObject != null){
}else {
myObject = new MyObject();
}
return myObject;
}
}
该代码在多线程环境中,会出现多个实例的情况,非安全。
解决办法:
- 声明关键字synchronized,但执行效率非常低下。
public class MyObject {
//立即加载方式
private static MyObject myObject;
private MyObject(){
}
synchronized public static MyObject getInstance() {
try{
if(myObject != null){
}else {
myObject = new MyObject();
}
}catch(Exception e ){
e.printStackTrace();
}
return myObject;
}
}
- 同步代码块,同上。
- 使用DCL双检查机制。成功解决。
public class MyObject {
//立即加载方式
private volatile static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance() {
try{
if(myObject != null){
}else {
synchronized(MyObject.class){
if(myObject == null){
myObject = new MyObject();
}
}
}
}catch(Exception e ){
e.printStackTrace();
}
return myObject;
}
}
2.3、静态内部类
public class MyObject {
//内部类
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
private MyObject(){}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
2.4、序列化问题
静态内部类可以达到线程安全,但是不能解决序列化时多个实例的问题。
public class MyObject {
//内部类
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
private MyObject(){}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
protected Object readResolve() throws ObjectStreamException {
return MyObjectHandler.myObject;
}
}
2.5、静态代码块
原理:静态代码块在使用类是已经执行了。
public class MyObject {
private static MyObject instance = null;
private MyObject(){}
static {
instance = new MyObject();
}
public static MyObject getInstance(){
return instance;
}
}
2.6、枚举类实现
原理:和静态代码块类似,在使用枚举类时,构造方法会自动调用。职责单一原则,应该隐藏枚举类。
public class MyObject {
public enum MyEnumSingleton {
connectionFactory;
private Connection connection;
private MyEnumSingleton() {
try{
....
}catch(){
}
}
public Connection getConnection(){
return connection;
}
}
public static Connection getConnection() {
return MyEnumSingleton.connectionFactory.getConnection();
}
}