作用是在指定的日期执行一次某一任务。
创建timer.task包,在包下创建MyTask 类
package timer.task;import java.util.TimerTask;public class MyTask extends TimerTask {@Overridepublic void run() {System.out.println("任务执行了,时间为:"+System.currentTimeMillis());}
} 创建timer.test包,在包下创建Test1类
package timer.test;import timer.task.MyTask;
import java.util.Date;
import java.util.Timer;public class Test1 {public static void main(String[] args) throws InterruptedException {long nowTime = System.currentTimeMillis();System.out.println("当前时间为:"+nowTime);long scheduleTime = nowTime + 10000;System.out.println("计划时间为:"+scheduleTime);MyTask task = new MyTask();Timer timer = new Timer();Thread.sleep(1000);/**1.调用Timer实例的schedule方法,将MyTask实例和将转换成Date对象的scheduleTime一起传入*/timer.schedule(task,new Date(scheduleTime));}
} 即先预先将计划的时间打印出来,然后当执行到schedule方法后,时间达到指定时间时执行task任务,task此时打印出来的时间会跟前者打印出来的时间相同。
任务虽然执行完了,但是进程还未被销毁,说明内部有非守护线程正在执行,即Timer的线程还在运行。
作用是终止此计时器,丢弃当前所有已安排的任务。这不会干扰当前正在执行的任务。一旦终止计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。
即安排任务的mainLoop方法跳出死循环,对执行任务的线程不会有影响。
创建timer.test包,在包下创建Test2类
package timer.test;import timer.task.MyTask;import java.util.Date;
import java.util.Timer;public class Test2 {public static void main(String[] args) throws InterruptedException {long nowTime = System.currentTimeMillis();System.out.println("当前时间为:"+nowTime);long scheduleTime = (nowTime + 15000);System.out.println("计划时间为:"+scheduleTime);MyTask myTask = new MyTask();Timer timer = new Timer();timer.schedule(myTask,new Date(scheduleTime));Thread.sleep(18000);timer.cancel();}
}
MyTask引用使用schedule方法时创建MyTask即可,调用timer.cancel()后实现TimerThread线程销毁。即18秒后,TimerThread线程安排任务的mainLoop方法跳出死循环,最后TimerThread线程被销毁。但是对执行任务的线程不会有影响。此时如果MyTask任务正在运行,则会等到MyTask任务结束,程序才会退出。
立即加载就是使用类的时候已经将对象创建完毕。常见的实现办法就是new实例化。也称饿汉模式。
实现方式为在类内部新建一个对象实例,外面直接通过一个方法获取这个对象实例。他的缺点是不能有其他实例变量,如果方法没有同步,有可能出现非线程安全问题。
创建包singleton.test,在包下创建类MyObject
package singleton.test;public class MyObject {//立即加载方法 == 饿汉模式private static MyObject myObject = new MyObject();private MyObject(){}public static MyObject getInstance(){return myObject;}
}
即使用静态内置类实现单例模式。
创建包singleton.thread,在包下创建类MyThread
package singleton.thread;import singleton.test.MyObject;public class MyThread extends Thread{@Overridepublic void run() {System.out.println(MyObject.getInstance().hashCode());}
}
创建包singleton.test.run,在包下创建类Run
package singleton.test.run;import singleton.thread.MyThread;public class Run {public static void main(String[] args) {MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();MyThread myThread3 = new MyThread();myThread1.start();myThread2.start();myThread3.start();}
} 输出的结果是相同的hashCode,证明是同一个实例(即单例),也就是该实例只能通过该对象的getInstance()方法来获取,并且不能new,以此来确保单例。
延迟加载就是调用get()方法时,实例才被工厂创建。常见的方法就是在get()方法中进行new实例化。也称懒汉模式。
package singleton.test;public class MyObject {//延迟加载private static MyObject myObject;private MyObject(){}synchronized public static MyObject getInstance(){if(myObject==null)myObject = new MyObject();return myObject;}
} 即在需要的时候才创建。但在多线程的环境中会出现取出多个实例的情况,所以直接在方法加上synchronized关键字,但是会导致方法效率低下。package singleton.test;public class MyObject {//延迟加载private volatile static MyObject myObject;private MyObject(){}public static MyObject getInstance(){if(myObject==null)synchronized (MyObject.class) {if (myObject == null)myObject = new MyObject();}return myObject;}
} volatile修饰MyObject ,作用是使该变量在多个线程间可见,从而确保在MyObject实例被new出来后能检测到myObject!=null。并且防止myObject = new MyObject()代码重排序。创建singleton.entity包,在包下创建UserInfo 类
package singleton.entity;public class UserInfo {} 创建singleton.test1包,在包下创建MyObject 类
package singleton.test1;import singleton.entity.UserInfo;import java.io.Serializable;public class MyObject implements Serializable {private static final long serialVersionUID = 1L;public static UserInfo userInfo = new UserInfo();private static MyObject myObject = new MyObject();private MyObject(){}public static MyObject getInstance(){return myObject;}protected Object readResolve(){System.out.println("调用了readResolve方法!");return MyObject.myObject;}
} 如果将单例对象进行序列化,使用默认的反序列化行为取出的对象是多例的。
上面的readResolve()方法作用是在反序列化时不创建新的MyObject对象,而是复用原有的MyOject对象。即没有使用该方法,反序列化时则会出现多个MyOject对象。
创建singleton.test1包,在包下创建SaveAndRead 类
package singleton.test1;import java.io.*;public class SaveAndRead {public static void main(String[] args) {try {MyObject myObject = MyObject.getInstance();System.out.println("序列化-myObject="+myObject.hashCode()+" userInfo="+myObject.userInfo.hashCode());FileOutputStream fos = new FileOutputStream(new File("myObject-File.txt"));ObjectOutputStream oos = new ObjectOutputStream(fos);//将myObject存入myObject-File.txt文件中oos.writeObject(myObject);} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}try {FileInputStream fis = new FileInputStream(new File("myObject-File.txt"));ObjectInputStream ois = new ObjectInputStream(fis);//将myObject从myObject-File.txt文件中取出MyObject myObject = (MyObject)ois.readObject();System.out.println("序列化-myObject="+myObject.hashCode()+" userInfo="+myObject.userInfo.hashCode());} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}
} 即通过MyOject.getInstance()方法获取MyOject对象后,将其读入myObject-File.txt文件中,然后再从该文件读取,使用readResolve()方法后,序列化和反序列化时,就不会创建新的MyOject对象,而是选择复用原有的MyOject对象,从而保持单例。不使用的话则会出现多个MyOject对象,即创建多个新的MyOject对象。
即在静态内置类实现单例的基础上进行序列化和反序列化。
但是如果将序列化和反序列化分别放入两个class,反序列化时会产生新的MyOject对象。放在两个class类中分别执行其实相当于创建了2个JVM虚拟机,每个虚拟机里有一个MyObject对象。原本想要实现的是在一个JVM虚拟机中进行序列化和反序列化时保持MyObject单例性。
因为静态代码块中的代码在使用类的时候就已经执行,所以可以应用静态代码块的这个特性实现单例模式。
package singleton.test;public class MyObject {private static MyObject myObject = null;private MyObject(){}static{myObject = new MyObject();}public static MyObject getInstance(){return myObject;}
} 即将new一个对象的代码改为放在static代码块中即可。枚举enum和静态代码块的特性相似。在使用枚举类时,构造方法会被自动调用,可以应用这个特性实现单例模式。
创建singleton.test2包,在包下创建枚举类MyObject
package singleton.test2;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class MyObject {public enum MyEnumSingleton{//枚举类的实例 不需要newconnectionFactory;private Connection connection;private MyEnumSingleton(){try{System.out.println("调用了MyObject的构造");String url = "jdbc:mysql:///test";String username="root";String password="123456";String driverName = "com.mysql.jdbc.Driver";//com.microsoft.sqlserver.jdbc.SQLServerDriverClass.forName(driverName);connection = DriverManager.getConnection(url,username,password);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (SQLException e) {throw new RuntimeException(e);}}}public static Connection getConnection(){return MyEnumSingleton.connectionFactory.connection;}
} 此时需要引用connector(mysql-connector-java-5.0.8-bin),即连接mysql所需要的connector的jar包。
即在构造方法中实例化一个connection对象。getConnection()方法返回这个对象。
创建singleton.thread包,在包下创建类MyThread2
package singleton.thread;import singleton.test2.MyObject;public class MyThread2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(MyObject.getConnection().hashCode());}}
} 创建singleton.test2.run包,在包下创建类Run
package singleton.test2.run;import singleton.thread.MyThread2;public class Run {public static void main(String[] args) {MyThread2 t1 = new MyThread2();MyThread2 t2 = new MyThread2();MyThread2 t3 = new MyThread2();t1.start();t2.start();t3.start();}
} 即启动线程,执行run方法,然后通过引用MyObject类的内置枚举类MyEnumSingleton的实例connectionFactory,再调用这个MyObject类的getConnection()方法来获取connection实例对象的hashCode。相同的hashCode,完成枚举类实现单例模式。