atomic 包、synchronized | Java 中线程安全
相关阅读
彻底搞懂 CPU 中的内存结构
Java 内存模型 ,一篇就够了!
多线程实现原理
之前已经说过了,我们在保证线程安全的时候主要就是保证线程安全的 3 大特性,原子性、可见性、有序性、而在 JMM 的层面也已经做了相关的操作,比方说 JMM 定义的 8 种原子操作以及相关的规则,happens-before 原则。
今天主要就来看看 Java 中实现线程安全的方法之二,使用 atomic 包,synchronized 关键字。
首先说说 AtomicInteger 这个类,我们来看一个例子,计数器。实现很简单,就是每个线程都过来加 1,我们期待的结果是 999,但是若不保证线程安全,结果往往不对。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicInt {
public static void main(String[] args) {
for(int i=0;i < 1000;i ++){
Thread thread = new Thread(new Counter());
thread.start();
}
System.out.println(Counter.getCount());
}
} class Counter implements Runnable{ // private static int count = 0;
private static AtomicInteger count = new AtomicInteger(0); public static int getCount(){
// return count;
return count.get();
} @Override
public void run() {
// count++;
count.incrementAndGet();
}
}
下面就来分析一下 incrementAndGet 方法的具体实现
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
} // var1 :count 对象;
// var2 :加数,count 中封装的整数;
// var4 :被加数 1;
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
// 该方法的主要目的取出主内存中的加数,【即为当前 count.get() 值】
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;
} public native int getIntVolatile(Object var1, long var2); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
compareAndSwapInt 方法既是我们经常看到的 CAS 的简写,但更多的是代表了一种思想。具体在这里该方法主要目的是比较我们从主存取出的整数值和我们从 count 中传过来的是否一致,count 中的整数也就是从线程的工作内存传过来的。若一致,则计算结果并返回。若不一致,为 var5 赋值【即修改主存中的值】,并继续从主存中取出 var5 ,继续比较,直到返回。
这中间的 CAS 的思想在其它的类中也很常用,理解其核心思想即可。比较工作内存和主存中的数据,使其一致再进行计算。
与上面类似的还有 AtomicBoolean 和 AtomicLong 对象,底层的实现还是 CAS。但是在 JDK1.8 中,出现了与 AtomicLong 类似的 LongAdder 对象。
我们知道,在 CAS 的实现中,主体部分在一个 while 循环中,会一直找到工作内存和主存一致的情况,若是竞争不激烈,这是没问题的,但是当竞争非常激烈的时候,一直返回不了结果,性能就会很差。CAS 也是一种乐观锁的表现,以为可以很快的找到并返回结果。
对于普通的 long 和 double 变量,JVM 必须将 64 位的读写操作拆成 2 个 32 位读写操作。而 LongAdder 这个类的实现基于的思想就是将【热点数据分离】,比方说可以将 AtomicLong 内部的 value 分离成一个数组,不同的线程根据 hash 可以操作不同的 cell ,最后再将整个数组中所有 cell 中的数累加。
这样做的结果就相当于在 AtomicLong 的基础上,将单点的压力,分散到不同的 cell 中。在低并发的时候可以不分离热点数据,使用 base 数据, 在高并发的分离数据,这样就保证了性能。缺点是 LongAdder 在统计的时候如果有并发更新,可能导致统计的数据有误差。
我们知道在 CAS 中,我们会持续的判断内存中的数和工作内存中是否一致,以此来判断有没有其它的线程修改了工作内存中的数据,但是存在一种情况,共享变量是 1 被修改为 2 ,而后又被修改为 1 ,此时已经线程不安全了。这个问题就是常说的 ABA 问题。
Java 中提供了 AtomicStampedReference<T>,这个类主要解决的就是 CAS 中 ABA 问题,为了解决 ABA 问题,引入了一个‘变量版本号’的概念,即每次修改版本号都会加 1。使用一个变量 stamp 来记录变量版本号。
AtomicBoolean 这个类中的 compareAndSet 方法还是比较常用的。用在标识变量 flag。若是某段代码只需要执行一次可以使用这个方法来做。
public class AtomicBooleanTest {
private static AtomicBoolean flag = new AtomicBoolean(false); public static void main(String[] args) { // 不管有多少个线程在执行,都能保证只有会一个线程执行下面这段代码
// 如果 flag 是 false,修改为 true
if(flag.compareAndSet(false, true)){
System.out.println("the flag has been changed ~");
// Work();
}
}
}
Java 中的锁有两种,一种是 synchronized 关键字,依赖于 JVM ,还有一种是代码层面的 Lock,依赖于特殊的 CPU 指令,常用的实现类有 ReentrantLock。
synchronized 可以用来修饰代码块和方法,而锁住的对象又可分为当前对象和类对象。当修饰静态方法和代码块(类.class)时为类锁。修饰一般方法和代码块(this)时为对象锁。
举例看看对象锁
public class SynTest { public static void main(String args[]) {
SyncThread s = new SyncThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
}
}
class SyncThread implements Runnable { private static int count = 0; public void run() {
// 修饰代码块,锁住当前对象:一个线程访问一个对象中的 synchronized(this) 同步代码块时,其他试图访问该对象的线程将被阻塞
synchronized (this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
}
}
另外 JMM 关于 synchronized 有两条规定:1 线程解锁前,必须把共享变量的最新值刷新到主存。2 线程解锁前,将清空工作内存中共享变量的值,从而使用共享变量时需要从主存中重新读取最新的值(注意,加锁与解锁是同一把锁)这也就保障了可见性。
atomic 包、synchronized | Java 中线程安全的更多相关文章
- java中线程机制
java中线程机制,一开始我们都用的单线程.现在接触到多线程了. 多线性首先要解决的问题是:创建线程,怎么创建线程的问题: 1.线程的创建: 四种常用的实现方法 1.继承Thread. Thread是 ...
- 沉淀再出发:java中线程池解析
沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...
- JAVA中线程同步方法
JAVA中线程同步方法 1 wait方法: 该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所 ...
- 多线程(三) java中线程的简单使用
java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...
- Java中线程同步的理解 - 其实应该叫做Java线程排队
Java中线程同步的理解 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可 ...
- Java中线程和线程池
Java中开启多线程的三种方式 1.通过继承Thread实现 public class ThreadDemo extends Thread{ public void run(){ System.out ...
- java中线程同步的理解(非常通俗易懂)
转载至:https://blog.csdn.net/u012179540/article/details/40685207 Java中线程同步的理解 我们可以在计算机上运行各种计算机软件程序.每一个运 ...
- 面试官:Java中线程是按什么顺序执行的?
摘要:Java中多线程并发的执行顺序历来是面试中的重点,掌握Java中线程的执行顺序不仅能够在面试中让你脱颖而出,更能够让你在平时的工作中,迅速定位由于多线程并发问题导致的"诡异" ...
- java中线程分两种,守护线程和用户线程。
java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...
随机推荐
- python - class propert应用
propert应用 #方式一: # class Student(object): # def __init__(self,name,sc): # self.name = name # self.sc ...
- 关于django1.7.7使用ajax后出现“CSRF token missing or incorrect”问题的解决办法
最近使用Python3.3.25和django1.7.7开发公司项目,在使用ajax来post数据时,居然一直提示:403错误,原因是“CSRF token missing or incorrect” ...
- 阿里云apache服务器外网无法访问(配置安全组,添加80服务)
CentOS的系统 ,已经安装好了 apache php mysql 常规排错过程(ps:没耐心的童鞋请直接看最后一步,学习在阿里云控制台配置 安全组,允许 http服务) 第一步:检查apache ...
- 【转】Zabbix 3.0 从入门到精通(zabbix使用详解)
[转]Zabbix 3.0 从入门到精通(zabbix使用详解) 第1章 zabbix监控 1.1 为什么要监控 在需要的时刻,提前提醒我们服务器出问题了 当出问题之后,可以找到问题的根源 网站/ ...
- SIFT feature
转载:http://www.cnblogs.com/wangguchangqing/p/4853263.html 1.SIFT概述 SIFT的全称是Scale Invariant Feature Tr ...
- ES系列四、ES6.3常用api之文档类api
1.Index API: 创建并建立索引 PUT twitter/tweet/ { "user" : "kimchy", "post_date&quo ...
- 转载:编译安装Nginx(1.5.1)《深入理解Nginx》(陶辉)
原文:https://book.2cto.com/201304/19618.html 1.5 configure详解 可以看出,configure命令至关重要,下文将详细介绍如何使用configure ...
- Linux中断处理(一)
最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理一下~~第一阶段--获取中断号每个 ...
- 如何查看centos系统cpu/内存使用情况
1.查看硬盘 [mushme@investide ~]$ df -ah 文件系统 容量 已用 可用 已用% 挂载点 /dev/cciss/c0d0p1 123G ...
- oracle进阶之connect by笔记
本博客是自己在学习和工作途中的积累与总结,仅供自己参考,也欢迎大家转载,转载时请注明出处. http://www.cnblogs.com/king-xg/p/6794562.html 如果觉得对您有帮 ...