Java 多线程 volitile 和 atomic
Java 多线程 volitile 和 atomic
volitile关键字
public class MTester {
    public static class TestKey{
        int x = 0;
    }
    public static TestKey key0 = new TestKey();
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (key0.x == 0){
            }
            System.out.println("key0"+key0.x);
        });
        Thread thread1 = new Thread(()->{
            try {
                Thread.sleep(1000);
                key0.x=1;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println("over");
            }
        });
        thread.start();
        thread1.start();
    }
}
尝试运行以上代码,发现thread永远也无法发现key0的x被改变

所以这个时候需要加上volitile关键字

具体原因是java中每个线程都有工作内存,以及主存
我的理解就是不加volitile,线程读写变量是先在自己的工作内存中处理,然后再写回主存,但是有的线程处理的是工作内存,但是并没有从主存里面读取,加上volitile关键字之后,会通知其他线程,让他们强制从主存中读取数据
https://www.cnblogs.com/zhengbin/p/5654805.html
volatile还有一个特性:禁止指令重排序优化。
可以见这个文章
https://www.cnblogs.com/chengxiao/p/6528109.html
但是volitile只能保证可见性,不能保证原子性,也就是说如果多线程操作 i++还是无法保证正确
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0;i<10;i++){
	service.submit(()->{
		for (int j = 0;j <10;j++){
			key0.x++;
			System.out.println(key0.x);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	});
}
service.awaitTermination(10, TimeUnit.SECONDS);
System.out.println(key0.x);

还是无法保证原子性
这个时候可以考虑使用 java.util.concurrent.atomic;中的类
这些类里面的类大部分都是使用CAS算法进行操作的
CAS compare and set
public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
这个 unsafe是unsafe类,里面的方法都是native方法
补充:

这个valueOffset可以理解为一个AtomicInteger的偏移量然后native方法然后传入native方法,这样就相当于获取了CAS需要比较对象的地址了
CAS其实就是期望的值进行比较,如果不相等,就证明有其他线程更改过了,然后不执行操作然后返回失败,CAS看起来很麻烦,但是却可以映射一些CPU指令,实际上执行起来还是很快的(参考java核心技术)
unsafe里面的方法大部分都是native方法

比如说我们想要对AtomicInteger执行一个 increase操作,就先比较自己跟期望的值,如果不等,那就在下次循环接着尝试更改,直到更改成功
//AtomicInteger.java
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
//Unsafe.class
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}
不过这种不断尝试比较,对CPU开销还是比较大,不过相对于synchronized来说更轻量级,因为synchronized需要不断尝试获取锁释放锁,而且只能独占
在并发量不是特别大的情况下,效率相对于synchronized还是很高的,当自选严重冲突的时候synchronized还是效率更高一些
CAS 算法属于自旋
不过CAS算法也有其他的缺点,常见的就是ABA问题
举个例子
线程1:从内存位置 1 取回A
线程2:从内存位置 1取到 A
线程2:做了一些操作
线程2:从内存位置 1 写入A
线程1:发现位置1还是A CAS成功但是却不知道线程2做了什么操作,可能引发一些后果
解决办法
AtomicStampedReference
可以用一个timestamp 或者mask来判断是否有其他操作
自旋锁的简单实现:
思路:每次只有一个线程进入临界区
import java.util.concurrent.atomic.AtomicReference;
public class MSpinLock {
    AtomicReference<Thread> reference = new AtomicReference<>();
    public void lock(){
        do {
        }while (!this.reference.compareAndSet(null,Thread.currentThread()));
    }
    public void unlock(){
        do {
        }while (!this.reference.compareAndSet(Thread.currentThread(),null));
    }
    public static void main(String[] args) {
        MSpinLock lock = new MSpinLock();
        Thread thread = new Thread(()->{
            while (true){
                try{
                    lock.lock();
                    System.out.println("thread had lock");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("thread will unlock");
                    lock.unlock();
                }
            }
        });
        Thread thread1 = new Thread(()->{
            while (true){
                try{
                    lock.lock();
                    System.out.println("thread1 had lock");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("thread1 will unlock");
                    lock.unlock();
                }
            }
        });
        thread.start();
        thread1.start();
    }
}

每次都是成对出现的
如果注释掉lock

显然不对
不过我们做的自选锁不可重入
假如有个函数需要递归,那么自旋锁就会发生死锁
所以我们需要一个Integer来判断一下
    public void lock(){
        if(Thread.currentThread().equals(reference.get())){
            atomicInteger.incrementAndGet();
            return ;
        }
        do {
        }while (!this.reference.compareAndSet(null,Thread.currentThread()));
        atomicInteger.incrementAndGet();
    }
    public void unlock(){
        if(Thread.currentThread().equals(reference.get())){
            int n = atomicInteger.decrementAndGet();
            if(n>0){
                return;
            }
        }
        do {
        }while (!this.reference.compareAndSet(Thread.currentThread(),null));
    }
这样就可重入了
https://www.cnblogs.com/qjjazry/p/6581568.html
Java 多线程 volitile 和 atomic的更多相关文章
- java多线程-cas及atomic
		
大纲: cas atomic 一.cas cas:compareAndSwap,一种乐观锁. cas思想:cas需要三个值,v是内存值,e是期望值,n是要修改的值.当内存中的值v等于预期值e(说明内存 ...
 - Java多线程系列九——Atomic类
		
参考资料:https://fangjian0423.github.io/2016/03/16/java-AtomicInteger-analysis/http://www.cnblogs.com/54 ...
 - java多线程系列5 atomic简介
		
先看一个例子,AtomicInteger 实现的线程安全的累加器 public class AtomicIntTest { public static void main(String[] args) ...
 - java多线程系列  目录
		
Java多线程系列1 线程创建以及状态切换 Java多线程系列2 线程常见方法介绍 Java多线程系列3 synchronized 关键词 Java多线程系列4 线程交互(wait和 ...
 - java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
		
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
 - Java 多线程:锁(一)
		
Java 多线程:锁(一) 作者:Grey 原文地址: 博客园:Java 多线程:锁(一) CSDN:Java 多线程:锁(一) CAS 比较与交换的意思 举个例子,内存有个值是 3,如果用 Java ...
 - 40个Java多线程问题总结
		
前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...
 - Java多线程系列--“JUC锁”03之 公平锁(一)
		
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
 - Java多线程系列--“JUC锁”04之 公平锁(二)
		
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
 
随机推荐
- [翻译] SVProgressHUD
			
SVProgressHUD https://github.com/TransitApp/SVProgressHUD SVProgressHUD is a clean and easy-to-use H ...
 - linux c编程调用系统的动态库时,要使用dlopen等函数吗?
			
同问 linux c编程调用系统的动态库时,要使用dlopen等函数吗? 2012-11-27 21:55 提问者: hnwlxyzhl 我来帮他解答 满意回答 2012-12-07 09:08 li ...
 - U-Mail:邮件营销如何大量获取并筛选有效地址
			
工欲善其事必先利其器,在所有的邮件推广中,最基础的工作就是收集到足够多的有效邮箱地址,俗话说韩信将兵多多益善,邮箱地址越多,也就意味着潜在的转化为消费者的数量众多,但是如果大量滥竽充数的地址被搜罗进来 ...
 - angularJs的工具方法2
			
一.angular.isArray 判断是否是数组 var a = []; console.log(angular.isArray(a)); //判断参数里面的是否是数组 二.angular. ...
 - μCOS-II系统之事件(event)的使用规则及Semaphore的相互排斥量使用方法
			
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/wavemcu/article/details/27798493 ****************** ...
 - jQuery UI 实例 – 切换(Toggle)
			
toggle()函数用于为每个匹配元素的click事件绑定轮流的处理函数. toggle()是一个特殊的事件函数,用于为匹配元素的click事件绑定多个事件处理函数.每次触发click事件时,togg ...
 - 2594. [WC2006]水管局长数据加强版【LCT+最小生成树】
			
Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一 ...
 - 查看oracle中表的索引
			
oracle中表的索引信息存在 user_indexes 和 user_ind_columns 两张表里面, 其中, user_indexes 系统视图存放是索引的名称以及该索引是否是唯一索引等信息, ...
 - 【JavaScript】插件参数的写法
			
就是实现复制的一个过程 (function() { var Explode = function(container, params) { 'use strict'; var n = this; if ...
 - [转]谈谈关于MVP模式中V-P交互问题
			
在差不多两年的时间内,我们项目组几十来号人都扑在一个项目上面.这是一个基于微软SCSF(Smart Client Software Factory)的项目,客户端是墨尔本一家事业单位.前两周,我奉命负 ...