其他小知识

发表时间:2017-09-07 22:34:23 浏览量( 21 ) 留言数( 0 )

学习目标:

1、了解Guava对线程池的封装

2、在项目中灵活使用Guava的线程池


学习过程:

一.SimpleDateFormat线程不安全性

  我们都知道在程序中我们应当尽量少的创建SimpleDateFormat 实例,因为创建这么一个实例需要耗费很大的代价。在一个读取数据库数据导出到excel文件的例子当中,每次处理一个时间信息的时候,就需要创建一个SimpleDateFormat实例对象,然后再丢弃这个对象。大量的对象就这样被创建出来,占用大量的内存和 jvm空间。代码如下:

public class DateUtil {

    

    public static  String formatDate(Date date)throws ParseException{

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

        return sdf.format(date);

    }

    

    public static Date parse(String strDate) throws ParseException{

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

        return sdf.parse(strDate);

    }

}


改写这个方法

public class DateUtil {

    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 class DateUtilTest {

    

    public static class TestSimpleDateFormatThreadSafe extends Thread {

        @Override

        public void run() {

            while(true) {

                try {

                    this.join(2000);

                } catch (InterruptedException e1) {

                    e1.printStackTrace();

                }

                try {

                    System.out.println(this.getName()+":"+DateUtil.parse("2013-05-24 06:02:20"));

                } catch (ParseException e) {

                    e.printStackTrace();

                }

            }

        }    

    }

    

    

    public static void main(String[] args) {

        for(int i = 0; i < 3; i++){

            new TestSimpleDateFormatThreadSafe().start();

        }

            

    }

}

    在正常的测试情况之下,都没有问题,但一旦在生产环境中一定负载情况下时,这个问题就出来了。他会出现各种不同的情况,比如转化的时间不正确,比如报错,比如线程被挂死等等。我们看下面的测试用例,那事实说话:


总结一下:在看SimpleDataFormat 源码时,提到说SimpleDataFormat不是线程安全的。

* 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的方法

 SimpleDateFormat 为什么不是线程安全的?

这是因为里面用了Calendar 这个成员变量来实现SimpleDataFormat,并且在Parse 和Format的时候对Calendar 进行了修改,calendar.clear(),calendar.setTime(date);

所以当SimpleDataFormat使用在多线程的环境中,我们要特别小心,有三种方法来解决这个问题:

1)每次使用时,都创建一个新的SimpleDateFormat实例。如果使用不是很频繁时,可以使用此方法,这样可以降低创建新对象的开销。

2)使用同步:不过,当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,这样的操作也会一定程度上影响性能。

3)就是使用ThreadLocal,来确保每个线程只有一个实例,这样可以保证在同一个线程中使用同一个SimpleDateFormat对象,不同线程使用不同的对象,既保证了效率,也不会有多线程的问题。

如:

public class ConcurrentDateUtil {


    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);

    }

}