zoukankan      html  css  js  c++  java
  • 逐步理解Java中的线程安全问题

    什么是Java的线程安全问题?

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读/写完,其他线程才可使用。不会出现数据不一致或者数据污染。

    线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

    那么,理解下上面这段话,再抛出新的问题:

    • 什么是脏数据?
    • 什么情况会触发线程安全问题?为什么静态变量和线程安全结合紧密?
    • 如何解决线程安全问题?
    • 如何预防线程安全问题?

    什么是脏数据?

    先百度:脏数据

    简单的说:

    通俗的讲,当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

    直观的感觉有点像冲突。

    什么情况会触发线程安全问题?为什么静态变量和线程安全结合紧密?

    去网上找答案和分析,很多情况会看到这么一句话:

    静态变量类型设置的不合理会造成线程不安全。

    黑人问号。。。

    我们来看下造成线程不安全的几个要素:

    • 多线程应用
    • 访问同一块/个数据;

    多线程应用:一般来说基本上都是了;

    访问同一块数据:这篇文章写的很好,转来分享:在多线程中使用静态方法是否有线程安全问题

    总而言之就是这样子的:——》调用静态方法——》调用静态变量——》线程不安全

    所以,直接引起线程不安全的是不安全的静态变量,前面并不重要;

    如何解决线程安全问题?

    对症下药:

    • 不安全的变量——》安全的变量;
    • 静态——》动态(每次使用时生成)

    后一个方法可以说是设计或者业务上的问题了,需要注意的是第一个,也就是哪些是线程安全,哪些线程不安全,还经常被用作静态变量。

    这里举两个碰到的例子:

    • SimpleDateFormat,不安全;
    • StringBuilder,不安全,StringBuffer,安全;

    另外,也可以对大量代码进行同步操作,但不是很推荐:

    1、同步方法

    给多线程访问的成员方法加上synchronized修饰符

    public void synchronized doWork(){
         // TODO
    }
    

    使用synchronized修饰的方法,就叫做同步方法,保证线程执行该方法的时候,其他线程只能在方法外等着。

    2、同步代码块

    synchronized(同步锁对象)
    {
         // 需要同步操作的代码
    }
    

    实际上,对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁,谁拿到锁,谁就可以进入代码块,其他线程只能在代码块外面等着,而且注意,在任何时候,Java虚拟机最多允许一个线程拥有该同步锁。

    Java程序运行可以使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象。

    实际上,同步方法和同步代码块差不了多少,在本质上是一样的,两者都用了一个关键字synchronized,synchronized保证了多线程并发访问时的同步操作,避免线程的安全性问题,但是有一个弊端,就是使用synchronized的方法/代码块的性能比不用要低一些,因此如果要用synchronized,建议尽量减小synchronized的作用域。

    如何预防线程安全问题?

    其实这里想说的是是否需要在开发时对线程安全问题重点考虑。

    为什么这么说呢?

    比如StringBuilder和StringBuffer,在相应的API文档中有这样的描述:

    将StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer。”,提到StringBuffer时,说到“StringBuffer是线程安全的可变字符序列,一个类似于String的字符串缓冲区,虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致”。StringBuilder是一个可变的字符序列,此类提供一个与StringBuffe兼容的API,但不保证同步。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。将StringBuilder的实例用于多个线程是不安全的,如果需要这样的同步,则建议使用StringBuffer。

    也就是说,一般推荐使用StringBuilder,这个不安全的家伙!!!

    因为它快!!!

    这里其实会有两个明显的疑问:

    • 快多少?
    • 会有静态的StringBuilder么?

    快多少?看这个:String、StringBuffer、StringBuilder的区别与效率比较

    结论是量级小看不出,量级大还是有区别。

    会有静态的StringBuilder么?我没想到...

    所以,线程安全并不是说开发中一直提心吊胆考虑的问题;

    简单来说,有静态变量了,多思考下应用场景,查一下前辈踩过的坑,问题基本避免了。

    至于衍生问题:为什么StringBuilder比StringBuffer快?源码告诉我们,后者有同步。

  • 相关阅读:
    linux --- 3 vim 网络 用户 权限 软连接 压缩 定时任务 yum源
    linux --- 2.常用命令 , python3, django安装
    linux --- 1.初始linux
    admin ---11.admin , 展示列表 和 分页
    并发 ---- 6. IO 多路复用
    django基础 -- 10.form , ModelForm ,modelformset
    django基础 -- 9.中间件
    flask基础
    MySQL-数据库增删改查
    面试题目二
  • 原文地址:https://www.cnblogs.com/andy1202go/p/8670447.html
Copyright © 2011-2022 走看看