SimpleDateFormate是一个线程不安全的类,在多线程中容易发生java.lang.NumberFormatException错误,如下:
public class SimpleDateFormatExample {
public static int clientTotle = 5000;
public static int threadTotal = 200;
public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotle);
for(int i = 0 ; i < clientTotle ; i++){
executorService.execute(()->{
try {
semaphore.acquire();
format();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
private static void format(){
try {
simpleDateFormat.parse("20180324");
} catch (ParseException e) {
e.printStackTrace();
}
}
}
代码解析:通过信号量和线程池模拟一个并发,在并发的时候SimpleDateFormat对象会发生错误.
解决思路:堆栈封闭原则
解析:在每次调用的时候都创建一个SimpleDateFormat的实例,如下:
public class SimpleDateFormatExample {
public static int clientTotle = 5000;
public static int threadTotal = 200;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotle);
for(int i = 0 ; i < clientTotle ; i++){
executorService.execute(()->{
try {
semaphore.acquire();
format();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
private static void format(){
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
simpleDateFormat.parse("20180324");
} catch (ParseException e) {
e.printStackTrace();
}
}
}
或者可以采用第三方包joda-time来解决:
首先导入第三方的包
joda-time官网
maven引入对应的包
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10</version>
</dependency>
如下使用方法:
public class SimpleDateFormatExample2 {
public static int clientTotle = 5000;
public static int threadTotal = 200;
private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyMMdd");
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotle);
for(int i = 0 ; i < clientTotle ; i++){
executorService.execute(()->{
try {
semaphore.acquire();
format();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
private static void format(){
long parseMillis = dateTimeFormatter.parseMillis("20180324");//parse("20180324");
System.out.println(parseMillis);
}
}
常用集合类的线程安全问题
首先模拟一个并发,我们会看到产生了线程安全问题:
public class SimpleDateFormatExample3 {
public static int clientTotle = 5000;
public static int threadTotal = 200;
private static List<Integer> list = new ArrayList<>();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotle);
for(int i = 0 ; i < clientTotle ; i++){
final int count = i;
executorService.execute(()->{
try {
semaphore.acquire();
add(count);
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println(list.size());
}
private static void add(int num){
list.add(num);
}
}
希望运行结构输出5000,结果不是我们所希望的那样,
发生了线程安全问题,同理hashSet和HashMap也会产生相同的问题
解决线程安全的容器有两种,一种是同步容器,一种是并发容器,这里推荐采用并发容器
J.U.C….