不好意思,一个国庆假期给我放的都不知道东西南北了,放松,很放松,差一点就弃更了,感谢那些催更的小伙伴们!

虽然没有更新,但是日常的学习还是有的,以后我尽量给大家分享一些通用知识,非技术。

但是本期还是要回归到之前的多前程的话题。已经说了线程和进程的区别、如何实现多线程、今天说一说线程中的安全问题。

首先明确一个概念,我们说线程安全是默认在多线程环境中,因为单线程中不存在线程安全问题。线程安全体现在多线程环境中程序的执行结果和单线程执行的结果一样。

那么多线程中会存在神马问题呢?举个例子来说,下面这一段代码中存在一个共享变量 count 。当程序调用 add 方法的时候,我们无法确定 count 的值是多少,因为它可能已经被其它线程 add 了很多次,或是正在被 add……

public class Obj {
private int count; public int add() {
return ++count;
}
}

这种情况是坚决不允许的!除非你就是想计算 add 方法被调用的次数。那我们要保证同一时间只能有一个线程访问共享资源,这也就是在实现线程安全。

如何实现呢?最常见的方式是加锁和同步。

使用锁机制可以保证同一时刻只有一个线程可以拿到锁进入临界区,保证了上锁和释放锁之间的这段代码同一时刻只能被一个线程执行。

public void testLock () {
lock.lock();
try{
int j = i;
i = j + 1;
} finally {
lock.unlock();
}
}

与锁类似的是同步方法或者同步代码块。使用非静态同步方法时,锁住的是当前实例;使用静态同步方法时,锁住的是该类的 Class 对象;使用静态代码块时,锁住的是 synchronized 关键字后面括号内的对象。

public void testLock () {
synchronized (anyObject){
int j = i;
i = j + 1;
}
}

无论使用锁还是 synchronized,本质都是一样,通过锁或同步来实现资源的排它性,从而实际目标代码段同一时间只会被一个线程执行,进而保证了目标代码段的原子性。这是一种以牺牲性能为代价的方法。

除了上面的两种方式还有一起其它的方法,比方说使用关键字 volatile 来修饰共享变量,或是保证每一步操作的原子性,但是在实际开发中不建议使用这些方式来保证线程的安全。

那我们来看一看,保证线程安全到底是在保证什么?底层的逻辑又是什么呢?

线程安全的三大特性,原子性、可见性、有序性。

原子性:类似于数据库中说的原子性,不可分割的操纵。这里说的就是对临界区代码的原子性操作,保证了线程的安全。加锁和同步都是在实现原子性。

可见性:当一个线程修改了变量后立即同步到主存中,让其它线程知道这个变量已经被改变了。就不会读取到过时的数据。

Java 提供了 volatile 关键字来保证可见性。当使用 volatile 修饰某个变量时,它会保证对该变量的修改会立即被更新到内存中,并且将其它线程缓存中对该变量的缓存设置成无效,因此其它线程需要读取该值时必须从主内存中读取,从而得到最新的值。

有序性:在程序的执行顺序和代码的编写顺序一样。这就是有序性,但是Java虚拟机为了优化,有一种重排序的机制,也就是说代码的执行顺序不一定是语句的顺序不一致,这在单线程中不会存在问题,JVM 会保证执行结果的正确。然而在多线程中,就会出现问题。

Java 中可通过 volatile 在一定程序上保证有序性,另外还可以通过 synchronized 和锁来保证有序性。

synchronized 和锁保证有序性的原理和保证原子性一样,都是通过保证同一时间只会有一个线程执行目标代码段来实现的。

JVM 为了优化执行顺序,提高性能。设计了重排序机制,然而这个机制在多线程中可能会带来问题,为了解决这个问题,我们可以通过代码保证有序性。这还不够, JVM 本身存在一个机制 happens-before(先行执行)来保证程序执行顺序。其余的代码就随 JVM 怎么弄了,目的提高性能。

先行执行机制中定义了好几条规则,拿出几条参观一下,你会感觉,呦,原来你认为很自然的事情是被这个规则所定义的!

1 被 volatile 修饰的写操作先发生于后面对该变量的读操作。

2 一个线程内,按照代码顺序执行。

3 Thread 对象的 start() 方法先发生于此线程的其它动作。

4 一个对象构造先于它的 finalize 发生。

……

总结一下,线程安全问题发生在多线程环境下对共享变量的操作中,为了防止出现数据状态不一致的情况,我们可以不在多线程中共享变量、将变量设置为 volatile 、使用同步或锁。其实也是在保证线程的特性不受破坏……

Java 中线程安全问题的更多相关文章

  1. java中线程安全问题

    在java中单线程和多线程是什么意思,他们有什么区别,分别的作用是什么? 在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”.多线程处理一个常见的例 ...

  2. Java基础-线程安全问题汇总

    Java基础-线程安全问题汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存泄漏和内存溢出(out of memory)的区别 1>.什么是内存溢出 答:内存溢出指 ...

  3. java中线程分两种,守护线程和用户线程。

    java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...

  4. java中线程机制

    java中线程机制,一开始我们都用的单线程.现在接触到多线程了. 多线性首先要解决的问题是:创建线程,怎么创建线程的问题: 1.线程的创建: 四种常用的实现方法 1.继承Thread. Thread是 ...

  5. Java中线程的使用 (2)-多线程、线程优先级、线程睡眠、让步、阻塞

    Java中线程的使用 (2)-多线程.线程优先级.线程睡眠.让步.阻塞 (一)多线程使用方法 说明:创建每个新的线程,一定要记得启动每个新的线程(调用.start()方法) class Xc3 ext ...

  6. Java中线程的实现:

    Java中线程的实现: 一.线程简介: 实现的两种方式为: 1.Thread类 2.Runnable接口 都在java.lang中 都有共通的方法:public void run() 二.线程常用方法 ...

  7. JAVA中线程同步方法

    JAVA中线程同步方法 1  wait方法:         该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所 ...

  8. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  9. Java中线程池,你真的会用吗?

    在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及基本原理. 在文中有这样一段描述: 可以通过Executors静态工厂构建线程池,但一般不建 ...

随机推荐

  1. 《PHP和MySQL Web开发》读书笔记(下篇)

    又与大家见面了.继续<PHP和MySQL Web开发>的总结. Chapter8.设计Web数据库 ·回去看看数据卡那本书吧,这里就不累赘谈这个东西. Chapter9.创建Web数据库 ...

  2. 纯javascript代码实现浏览器图片选择预览、旋转、批量上传

    工作中遇到的业务场景,和同事一起研究了下,主要是为了兼容IE版本 其实就是一些琐碎的知识点在网上搜集下解决方式,然后集成了下,主要有以下点: 1. IE input type=file的图片预览要用I ...

  3. 【专题】计数问题(排列组合,容斥原理,Prufer序列)

    [容斥原理] 对于统计指定排列方案数的问题,一个方案是空间中的一个元素. 定义集合x是满足排列中第x个数的限定条件的方案集合,设排列长度为S,则一共S个集合. 容斥原理的本质是考虑[集合交 或 集合交 ...

  4. linux下lz4解压缩遇到的那些事儿

    一.Debian系列:Debian.Ubuntu等1.1 kali下修改apt-get源:   vim /etc/apt/sources.list     deb http://mirrors.ust ...

  5. js 禁用右键菜单、拖拽、选中、复制

    //禁用拖拽 document.ondragstart = function () { return false; }; /** * 禁用右键菜单 */ document.oncontextmenu ...

  6. Mysql 监控性能状态 QPS/TPS【转】

    QPS(Query per second) 每秒查询量 TPS(Transaction per second)每秒事务量 这是Mysql的两个重要性能指标,需要经常查看,和Mysql基准测试的结果对比 ...

  7. Python学习1-Python和Pycharm的下载与安装

    本文主要介绍Python的下载安装和Python编辑器Pycharm的下载与安装. 一.Python的下载与安装 1.下载 到Python官网上下载Python的安装文件,进入网站后显示如下图: 网速 ...

  8. java多线程-读写锁原理

    Java5 在 java.util.concurrent 包中已经包含了读写锁.尽管如此,我们还是应该了解其实现背后的原理. 读/写锁的 Java 实现(Read / Write Lock Java ...

  9. UVA 10891 Game of Sum(区间DP(记忆化搜索))

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  10. HDU 1669 Jamie's Contact Groups(多重匹配+二分枚举)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1669 题目大意: 给你各个人可以属于的组,把这些人分组,使这些组中人数最多的组人数最少,并输出这个人数 ...