1、SimpleDateFormat不是线程安全的对象
举个栗子:
@Slf4j
public class DateFormatExample1 {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static StringBuffer stringBuffer = new StringBuffer();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i< clientTotal; i ++) {
executorService.execute(()->{
try {
semaphore.acquire();
update();
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
public static void update() {
try {
simpleDateFormat.parse("20180208");
} catch (ParseException e) {
log.error("parse exception", e);
}
}
}
输出:
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.example.concurrent.commonUnsafe.DateFormatExample1.update(DateFormatExample1.java:47)
at com.example.concurrent.commonUnsafe.DateFormatExample1.lambda$main$0(DateFormatExample1.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
2、改进上述SimpleDateFormat的用法(变为线程安全的)
改为局部变量(因为局部变量是堆栈封闭,线程安全的)每次声明一个新的变量来使用。
如下:
@Slf4j
public class DateFormatExample2 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static StringBuffer stringBuffer = new StringBuffer();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i< clientTotal; i ++) {
executorService.execute(()->{
try {
semaphore.acquire();
update();
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
public static void update() {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
simpleDateFormat.parse("20180208");
} catch (ParseException e) {
log.error("parse exception", e);
}
}
}
3、利用JodaTime(线程安全的) (
推荐用这个,不关是因为线程安全的
)
如下:
@Slf4j
public class DateFormatExample3 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyMMdd");
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i< clientTotal; i ++) {
final int count = i;
executorService.execute(()->{
try {
semaphore.acquire();
update(count);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
public static void update(int i) {
log.info("{},{}", i, DateTime.parse("20180208").toDate());
}
}