单例设计模式
单例设计模式
比较常见的有spring提供的ioc,控制翻转,只需要加一个componet注解,默认注解修饰的对象就是单例的
如果自己要实现一个单例,应该是如何一个思路呢
借鉴xxl-job里面客户端的代码
功能:一个线程后台起,每隔1天去扫描一下日志文件,只保留最近的30个日志文件的功能
代码样例
public class JobLogFileCleanThread {
private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class);
private static JobLogFileCleanThread instance = new JobLogFileCleanThread();
public static JobLogFileCleanThread getInstance(){
return instance;
}
private Thread localThread;
private volatile boolean toStop = false;
public void start(final long logRetentionDays){
// limit min value
if (logRetentionDays < 3 ) {
return;
}
localThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// clean log dir, over logRetentionDays
File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
if (childDirs!=null && childDirs.length>0) {
// today
Calendar todayCal = Calendar.getInstance();
todayCal.set(Calendar.HOUR_OF_DAY,0);
todayCal.set(Calendar.MINUTE,0);
todayCal.set(Calendar.SECOND,0);
todayCal.set(Calendar.MILLISECOND,0);
Date todayDate = todayCal.getTime();
for (File childFile: childDirs) {
// valid
if (!childFile.isDirectory()) {
continue;
}
if (childFile.getName().indexOf("-") == -1) {
continue;
}
// file create date
Date logFileCreateDate = null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
logFileCreateDate = simpleDateFormat.parse(childFile.getName());
} catch (ParseException e) {
logger.error(e.getMessage(), e);
}
if (logFileCreateDate == null) {
continue;
}
if ((todayDate.getTime()-logFileCreateDate.getTime()) >= logRetentionDays * (24 * 60 * 60 * 1000) ) {
FileUtil.deleteRecursively(childFile);
}
}
}
} catch (Exception e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
try {
TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destory.");
}
});
localThread.setDaemon(true);
localThread.setName("xxl-job, executor JobLogFileCleanThread");
localThread.start();
}
public void toStop() {
toStop = true;
if (localThread == null) {
return;
}
// interrupt and wait
localThread.interrupt();
try {
localThread.join();
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
代码分析
- JobLogFileCleanThread 类就是一个单例模式,内部有一个静态变量instance,默认值是new JobLogFileCleanThread()自己本身
- 静态方法getInstance()取到的变量就是静态变量instance
- 为什么这个JobLogFileCleanThread的线程类自己本身没有继承Thread父类呢,而是里面又内嵌了一个Thread类型的localThread变量?
- JobLogFileCleanThread 自己有一个start方法,且是可以传递参数的,默认线程类Thread的start是不可以接受参数的,所以JobLogFileCleanThread类自己本身没有继承Thread类
- 可以看出start(long logRetentionDays)方法,内部起了一个new Thread的匿名内部类,并赋值给了变量localThread,匿名内部类里面写自己的业务逻辑,并且是个死循环,依赖一个flag标记,这个flag标记由voliate修饰,杜绝线程之间可见性问题也可以做到执行方法stop的时候,可以优雅关闭死循环的线程。
原创:做时间的朋友