回顾一下以前的那套日期时间API,你就能发现它是线程不安全的,是可变的。这里就以传统日期时间格式化为例,看看它存在什么多线程安全问题?
如果我们想要使用SimpleDateFormat类来对一个时间或者日期进行格式化,并且还要使用多线程来操作,即使用多线程同时对一个时间或者日期进行格式化,那么该咋办呢?我们可以创建一个线程池,然后分10次去访问定义好的一个任务(该任务就是专门用于格式化一个时间或者日期的),都来解析某个时间或者日期。
package com.meimeixia.java8;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 这里就以传统日期时间格式化为例,看看它存在什么多线程安全问题?
* @author liayun
*
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//定义好如下一个任务(task),该任务就是专门用于格式化一个时间或者日期的
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20191207");
}
};
//创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<Future<Date>>();
//分10次去访问以上定义好的任务(task),然后它就会返回一个结果(叫Future),结果我都给它放在上面的集合里面
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
}
}
运行以上程序,会发现报了如下错误,日期已经格式化不下去了。这说明已经存在多线程安全问题了,也就是说SimpleDateFormat类或者传统的时间日期API均存在多线程安全问题。
抛开Java 8来说的话,要是以前,这个问题该怎么解决呢?要想解决这个多线程安全问题,就得上锁。这就引出了一系列的问题,对什么上锁?怎么上锁?上什么锁?一句话搞定,使用ThreadLocal类对以上程序中的sdf变量上锁,说白了,ThreadLocal类就可以锁这个变量。那怎么怎么上锁呢?
package com.meimeixia.java8;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatThreadLocal {
/*
* ThreadLocal说白了,是不是就可以锁这个变量啊?
*/
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
//ThreadLocal里面有一个供子类继承的方法,即initialValue()
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
然后,使用被ThreadLocal锁上的SimpleDateFormat来格式化一个时间或者日期。
package com.meimeixia.java8;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 这里就以传统日期时间格式化为例,看看它存在什么多线程安全问题?
* @author liayun
*
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
//定义好如下一个任务(task),该任务就是专门用于格式化一个时间或者日期的
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
//这次的SimpleDateFormat是不是被ThreadLocal给锁上了啊!
return DateFormatThreadLocal.convert("20191205");
}
};
//创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<Future<Date>>();
//分10次去访问以上定义好的任务(task),然后它就会返回一个结果(叫Future),结果我都给它放在上面的集合里面
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
//运行以上程序,发现程序没停下来!为什么啊?池忘记关了!
pool.shutdown();
}
}
此时,运行以上程序,你便能在Eclipse控制台看到如下打印内容了。
这说明通过上锁解决了传统日期时间格式化的多线程安全问题。
而现在,使用Java 8中这套全新的日期时间API之后,就没有什么多线程安全问题了,因为你不管做什么样的改变,它都会给你产生一个全新的实例,所以说它是线程安全的,这样就解决了多线程的安全问题。
package com.meimeixia.java8;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 这里就以传统日期时间格式化为例,看看它存在什么多线程安全问题?
* @author liayun
*
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
// DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;//常用的格式
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");//指定自己指定的格式
//现在,Java 8以后,这套全新的日期时间API(例如LocalDate、LocalTime、LocalDateTime类)中类的实例完全就是不可变的对象,这就解决了多线程安全问题
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
//使用Java 8中这套全新的日期时间API之后,就没有什么多线程安全问题了,因为你不管做什么样的改变,它都会给你产生一个全新的实例,所以说它是线程安全的,这样就解决了多线程的安全问题。
return LocalDate.parse("20191203", dtf);
}
};
//创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results = new ArrayList<Future<LocalDate>>();
//分10次去访问以上定义好的任务(task),然后它就会返回一个结果(叫Future),结果我都给它放在上面的集合里面
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<LocalDate> future : results) {
System.out.println(future.get());
}
//运行以上程序,发现程序没停下来!为什么啊?池忘记关了!
pool.shutdown();
}
}
此时,运行以上程序,你便能在Eclipse控制台看到如下打印内容了。
这就说明了Java 8中这套全新的日期时间API是线程安全的。