博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Concurrency(一)
阅读量:7079 次
发布时间:2019-06-28

本文共 5149 字,大约阅读时间需要 17 分钟。

hot3.png

现在在CPU上,已经失效,大家都不再追求高频率,而是越来越追求多核,似乎更重要一些了。

并发有很多优点,包括充分利用CPU的多核提高性能,更简单的模型(跟异步的相比)以及响应更灵敏得GUI等。

但是并发也引出很多问题,最重要的是安全性,很多在单线程环境下理所当然正确的程序在并发下都变得不正确,为了写出并发安全的程序,需要付出更多的努力。本文主要介绍一下并发问题的根本原因,以及针对策略。

来看下面简单的代码,我们知道SimpleDateFormat比较费时,所以很多程序里面会定义一个static的SimpleDateFormat,但是奇怪的事情发生了。

package concurrentStudy;import java.text.ParseException;import java.text.SimpleDateFormat;/** * Created by magicalli on 2014/12/13. */public class SimpleDateFormatTest01 {    private static final SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    try {                        System.out.println(dateformat.parse("2014-12-19 11:21:21"));                    } catch (ParseException e) {                        e.printStackTrace();                    }                }            }).start();        }    }}
模拟多个线程下会用到dateformat这个变量,运行之后发现很多错误,有抛出异常的,有结果错误的,如下图

究其原因,SimpleDateFormat不是线程安全的。只是SimpleDateFormat的doc(据说在JDK6之后才加进去的?)

* 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.
并发安全,主要由于:
  1. 多个线程
  2. 并发访问共享状态
  3. 并且状态可被修改

要想保证并发安全,上述3个条件,只能满足2个,于是我们有了3种解决方法(C(3, 1) == 3)。

第一种,不要多个线程访问,即加同步或者显示锁:

package concurrentStudy;import java.text.ParseException;import java.text.SimpleDateFormat;/** * Created by magicalli on 2014/12/13. */public class SimpleDateFormatTest01 {    private static final SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    private static final Object lock = new Object();    public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    try {                        synchronized (lock) {                            System.out.println(dateformat.parse("2014-12-19 11:21:21"));                        }                    } catch (ParseException e) {                        e.printStackTrace();                    }                }            }).start();        }    }}
第二种方法,不要共享变量,即“线程封闭(Thread Confinement),又大概有两种,一是栈封闭,即使用局部变量,方法里面自己new一个SimpleDateFormat出来
package concurrentStudy;import java.text.ParseException;import java.text.SimpleDateFormat;/** * Created by magicalli on 2014/12/13. */public class SimpleDateFormatTest03 {    public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    try {                        final SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");                        System.out.println(dateformat.parse("2014-12-19 11:21:21"));                    } catch (ParseException e) {                        e.printStackTrace();                    }                }            }).start();        }    }}
但是这个有一个坏处,我们一开始说用static的目的是因为new一个SimpleDateFormat比较费时,如果在每次调用的时候new一个出来,感觉有点浪费,于是有了ThreadLocal这个神器。ThreadLocal可以简单理解为一个Map<Thread, T>,即以线程为key的map,这样可以保证每个线程有自己的SimpleDateFormat,避免了各个线程共享。
package concurrentStudy;import java.text.ParseException;import java.text.SimpleDateFormat;/** * Created by magicalli on 2014/12/13. */public class SimpleDateFormatTest04 {    private static final ThreadLocal
dateformat = new ThreadLocal
() { @Override protected SimpleDateFormat initialValue() { System.out.println("init......."); return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { try { System.out.println(dateformat.get().parse("2014-12-19 11:21:21")); } catch (ParseException e) { e.printStackTrace(); } } } }).start(); } }}

运行结果可以看到,即使调用了100次,也只针对每个线程new了对象,总共只有10个SimpleDateFormat对象,大大减小了开销。

其实更好的解决方法就是,不用JDK自带的Date等一切日期类!!!用Joda-time,api更友好,更简单易用,功能更强大!

package concurrentStudy;import org.joda.time.format.DateTimeFormat;import org.joda.time.format.DateTimeFormatter;/** * Created by magicalli on 2014/12/13. */public class SimpleDateFormatTest05 {    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");    public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    System.out.println(dateTimeFormatter.parseDateTime("2014-12-19 11:21:21"));                }            }).start();        }    }}

转载于:https://my.oschina.net/magicly007/blog/357969

你可能感兴趣的文章
windos 8 虚拟光驱/硬盘技术
查看>>
WLC和汇聚交换机的配置
查看>>
使用python-gitlab的API V4来批量创建projects
查看>>
CLR.Via第三版第二章 生成、打包、部署和管理i应用程序及类型(
查看>>
关于ha高可用性的安装,ClusterIP和tomcat的配置
查看>>
我的系统我做主-----深度裁剪红帽5.8系统过程演示(只有5M哦)
查看>>
Zend server最大化应用程序的性能、扩展性和可用性
查看>>
Mac OSX操作系统安装和配置Zend Server 6教程(4)
查看>>
python进阶学习路线(全)
查看>>
浏览器加载与渲染
查看>>
HTTP常见错误返回代码
查看>>
安装homeassistant+python3.6
查看>>
老李分享:JDK,JRE,JVM区别与联系 1
查看>>
CentOS 7 上systemctl 的用法
查看>>
Android Design框架
查看>>
[Linux] 在 Linux CLI 使用 ssh-keygen 生成 RSA 密钥
查看>>
在 Ubuntu 16.04 Server 上安装 Zabbix
查看>>
Netgear wndr3700v2 路由器刷OpenWrt打造全能服务器(九)ftp服务
查看>>
oracle 存储过程的基本语法
查看>>
程序员应该遵守的编程原则
查看>>