Java 高级编程之避坑指南

Java 高级编程之避坑指南

  • 1. 不要使用 SimpleDateFormat
    • 1.1 常见的错误使用方式
    • 1.2 解决方案 1
    • 1.3 解决方案 2
  • 2. 分布式唯一ID生成器UidGenerator

1. 不要使用 SimpleDateFormat

1.1 常见的错误使用方式

一般我们使用SimpleDateFormat的时候会把它定义为一个静态变量,避免频繁创建它的对象实例,如下代码:
单线程场景使用时没有问题,多线程场景下使用时会报错:java.lang.NumberFormatException: multiple points
单线程

  • Date formats are not synchronized.
  • It is recommended to create separate format instances for each thread.
  • If multiple threads access a format concurrently, it must be synchronized externally.
    此外,SimpleDateFormat和它继承的DateFormat类也不是线程安全的。
public class SimpleDateFormatTest {

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String formatDate(Date date) throws ParseException {
        return sdf.format(date);
    }

    public static Date parse(String strDate) throws ParseException {
        return sdf.parse(strDate);
    }

    public static void main(String[] args) throws InterruptedException, ParseException {

        System.out.println(sdf.format(new Date()));
        
    }

	public static void main(String[] args) throws InterruptedException, ParseException {

	    ExecutorService service = Executors.newFixedThreadPool(100);
	
	    for (int i = 0; i < 20; i++) {
	        service.execute(() -> {
	            for (int j = 0; j < 10; j++) {
	                try {
	                    System.out.println(parse("2018-01-02 09:45:59"));
	                } catch (ParseException e) {
	                    e.printStackTrace();
	                }
	            }
	        });
	    }
	    // 等待上述的线程执行完
	    service.shutdown();
	    service.awaitTermination(1, TimeUnit.DAYS);
	}
}

低效的解决方案有:1)每次都生成一个新的对象,缺点是低效,创建大量的临时对象。2)使用 synchronized 关键字,缺点是:降低了并发性,大量并发时进程阻塞。下面两种是推荐的可选方式,更推荐方案 2.

1.2 解决方案 1

ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象。

private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }
};

public static Date parse(String dateStr) throws ParseException {
    return threadLocal.get().parse(dateStr);
}

public static String format(Date date) {
    return threadLocal.get().format(date);
}

1.3 解决方案 2

DateTimeFormatter:

  • This class is immutable and thread-safe.
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SimpleDateFormatTest {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static String formatDate2(LocalDateTime date) {
        return formatter.format(date);
    }

    public static LocalDateTime parse2(String dateNow) {
        return LocalDateTime.parse(dateNow, formatter);
    }

    public static void main(String[] args) throws InterruptedException, ParseException {

        ExecutorService service = Executors.newFixedThreadPool(100);

        // 20个线程
        for (int i = 0; i < 20; i++) {
            service.execute(() -> {
                for (int j = 0; j < 10; j++) {
                    try {
                        System.out.println(parse2(formatDate2(LocalDateTime.now())));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        // 等待上述的线程执行完
        service.shutdown();
        service.awaitTermination(1, TimeUnit.DAYS);


    }
}

2. 分布式唯一ID生成器UidGenerator

百度开源的分布式唯一ID生成器UidGenerator,解决了时钟回拨问题

你可能感兴趣的:(Java)