轻量级同步机制:volative关键字

volative的作用

关键作用是使变量在多个线程之间可见

public class VolativeText {
public static void main(String[] args) throws InterruptedException {
Student student=new Student();
new Thread(new Runnable() {
@Override
public void run() {
student.GetMethon();
}
}).start();
Thread.sleep(1000);//睡眠之后修改布尔值
student.GetName(false);//改变布尔值以结束程序运行
}
static class Student
{
public boolean flag=true;
public Student GetName(boolean flag)
{
this.flag=flag;
return this;
}
public void GetMethon()
{
System.out.println("开始");
while (flag){//死循环
}
System.out.println("结束");
}
}
}

程序并没有因为我修改之后结束运行,因为线程对共享变量具有不可见性,main线程修改布尔值之后,子线程看不到值的修改。因此要想实现线程的可见性这里可以加上volative关键字修饰公共变量

volative关键字的作用:使线程在强制公共内存中读取变量值,保证可见性

volatile非原子特性

public class Text10 extends  Thread {
private volatile static int count;
@Override
public void run() {
Addcount();
}
public static void Addcount()
{
for (int i = 0; i < 1000; i++) {
count++;
}
System.out.println(Thread.currentThread().getName()+"-->"+count);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Text10 text10=new Text10();
text10.start();
}
}
}

按道理输出1000的整数倍数才对,但是变量在自增的过程中没有更新到又被读取再修改,因此volatile不具备原子性,正确办法将方法加上synchronized关键字

volatile与synchronized比较

  • volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,volatile只能修饰变量,而synchronized可以修饰方法代码块,在开发中使用synchronized比例还是挺大的。

  • 多线程访问volatile变量不会发生阻塞,而synchronized可能会阻塞。

  • volatile能保证数据的可见性,但是不能保证原子性,而synchronized可以保证原子性,也可以保证可见性,因为

  • synchronized会将线程的工作内存和主内存进行同步

  • volatile关键字保证多个线程之间的可见性,synchronized关键字解决线程访问公共资源的同步性。

    区别 synchronized volatile
    使用上 只能用于修饰方法、代码块 只能修饰实例变量或者类关键字
    原子性保证 能保证,锁可以保护数据不被打断 无法保证
    可见性保证 能保证,排它方式使同步代码串行 能保证,可以读取公共变量
    有序性保证 能保证,在同步串行的时候 能保证,禁止JVM以及处理器进行排序
    阻塞情况 会发生阻塞 不会发生阻塞

常用原子类进行自增自减操作

i++不是原子操作,除了使用synchronized进行同步,也可以使用AtomicInteger/AtomicLong进行实现

import java.util.concurrent.atomic.AtomicInteger;
public class Text10 extends Thread {
private static AtomicInteger count=new AtomicInteger();
@Override
public void run() {
AddCount();
}
public static void AddCount()
{
for (int i = 0; i < 1000; i++) {
count.getAndIncrement();//相当于i++
//count.incrementAndGet();//相当于++i
}
System.out.println(Thread.currentThread().getName()+"-->"+count.get());
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Text10 text10=new Text10();
text10.start();
} }
}

CAS

CAS(Compare And Swap)是由硬件实现的,

CAS可以将read(读)-modify(修改)-write(写)转化为原子操作

i++自增过程:

从主内存调取i变量值

对i值加1

再把加1过后的值保存到主内存

CAS原理:在把数据更新到主内存时,再次读取主内存变量的值,如果现在变量的值与期望的值一样就更新。

使用CAS原理实现线程安全计数器

public class CASText {
public static void main(String[] args) {
CASControl casControl=new CASControl();
for (int i = 0; i <10000 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"==>"+casControl.incrementAndGet());
}
}).start();
}
}
}
class CASControl
{
volatile static long value;//使用volatile修饰线程可见性
public long getValue()
{
return value;
}
private boolean Expect(long oldValue,long newValue)
{
synchronized (this)
{
if(value==oldValue)
{
value=newValue;
return true;
}
else
return false;
}
}
public long incrementAndGet()
{
long oldvalue;
long newValue;
do {
oldvalue=value;
newValue=oldvalue+1; }while (!Expect(oldvalue, newValue));
return newValue;
}
}

CAS中的ABA问题

CAS实现原子操作背后有一个假设:共享变量当前值与当前线程提供的期望值相同,就认为变量没有被其他线程过。

实际上这种假设不一定成立,假如count=0

A线程对count值修改为10

B线程对count值修改为30

C线程对count值修改为0

当前线程看到count=0,不能认为count没有被其他线程更新,这种结果是否能被接受?

这就是CAS中的ABS问题即共享变量经过了A=》B=》A的更改

是否能够接受ABA问题跟算法实现有关

如果想要规避ABA问题,可以为共享变量引入一个修订号,或者时间戳,每次修改共享变量值,相应的修订号加1.,就会变更为[A,0]=>[B,1]=>[A,2],每次对共享变量的修改都会导致共享变量的增加,通过这个标识就可以判断。AtomicStampedReference类就是基于这个思想产生的。

原子变量类

原子类变量是基于CAS实现的,当对共享变量进行read(读)-modify(修改)-write(写)操作时,通过原子类变量可以保障原子性与可见性,对变量的read(读)-modify(修改)-write(写)操作不是指一个简单的赋值,而是变量的新值,依赖变量的旧值,如自增操作i++,由于volatile只能保证原子的可见性,而不能保证原子性,原变量类内部就是一个借助volatile变量,并且保障了该变量的read-modify-wirte操作的原子性,有时把原子变量看作一个增强的volatile变量,原子变量类有12个

分组 原子变量类
基础数据型 AtomicInteger、AtomicLong、AtomicBoolean
数组型 AtomicIntegerArry、AtomicLongArry、AtomicReferenceArry
字段更新器 AtomocIntegerFiledUpdater、AtomicLongFieldUpdate、AtomicReferenceFiledUpdater
引用型 AtomicReference、AtomicStampedReference、AtomicMarkableReference

使用AtomicLong定义计数器

import java.util.Random;
import java.util.concurrent.atomic.AtomicLong; public class Text15 {
//构造方法私有化
private Text15(){}
//私有静态对象
private static final Text15 text=new Text15();
//公共静态方法返回该类的实例
public static Text15 getInstance()
{
return text;
}
//使用原子类记录保存请求总数 成功数 失败数
private final AtomicLong RequestCount=new AtomicLong(0);
private final AtomicLong SuccessCount=new AtomicLong(0);
private final AtomicLong FailCount=new AtomicLong(0);
//进行自增
public void RequestCount()
{
RequestCount.incrementAndGet();
}
public void SuccessCount()
{
SuccessCount.incrementAndGet();
}
public void FailCount()
{
FailCount.incrementAndGet();
}
//查看总数
public long GetRequestCount()
{
return RequestCount.get();
}
public long GetSuccessCount()
{
return SuccessCount.get();
}
public long GetFailCount()
{
return FailCount.get();
}
}
class Text16
{
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
Text15.getInstance().RequestCount();//请求数量
int num=new Random().nextInt();
if(num%2==0)//如果是偶数就成功
{
Text15.getInstance().SuccessCount();
}
else
Text15.getInstance().FailCount();
}
}
}).start();
Thread.sleep(1000);
System.out.println("请求总数:"+Text15.getInstance().GetRequestCount());
System.out.println("请求成功"+Text15.getInstance().GetSuccessCount());
System.out.println("请求失败"+Text15.getInstance().GetFailCount());
}
}

多线程之volative关键字的更多相关文章

  1. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  2. Java多线程之volatile关键字《一》

    关键字volatile的主要作用是使变量在多个线程间可见. 1.关键字volatile与死循环 如果不是在多继承的情况下,使用继承Thread类和实现Runnable接口在取得程序运行的结果上并没有什 ...

  3. 多线程之volatile关键字(五)

    开始全文之前,先铺垫一下jvm基础知识以及线程栈: JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolea ...

  4. java多线程之volatile关键字

    public class ThreadVolatile extends Thread { public boolean flag=true; @Override public void run() { ...

  5. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  6. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  7. python 线程之_thread

    python 线程之_thread _thread module: 基本用法: def child(tid): print("hello from child",tid) _thr ...

  8. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  9. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

随机推荐

  1. Leetcode(17)-电话号码的字母组合

    给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23" 输出:[&quo ...

  2. 计算机网络 part2

    一.UDP协议 1.概述 UDP提供不可靠的服务,无连接(不存在建立连接的时延),首部开销相对TCP小,没有拥塞控制,提供最大努力交付,面向报文(无论多长的报文UDP也只加一个头部就往下发:TCP面向 ...

  3. 基础命令使用[win篇]

        基础命令使用 [ win篇 ] 2017-11-05 WIN CMD 0x01 基础命令使用: 演示环境: 1 2 win2008R2cn ip: 192.168.3.23 假设为入侵者机器 ...

  4. 翻译:《实用的Python编程》01_05_Lists

    目录 | 上一节 (1.4 字符串) | 下一节 (1.6 文件) 1.5 列表 本节介绍 Python 原始数据类型列表(list). 列表是一种有序的集合. 创建列表 使用方括号 [] 来定义列表 ...

  5. CSS ? Layout Module : CSS 布局模型

    1 1 1 https://www.w3.org/TR/css-grid-1/ CSS Grid Layout Module Level 1 W3C Working Draft, 19 May 201 ...

  6. how to read the 10th line of a text using shell script

    how to read the 10th line of a text using shell script shell script / bash script question https://l ...

  7. Keep Fitness

    Keep Fitness 健身 keep health 训练流程 Part 1 热身 5-10分钟 Part 2 肌肉力量训练 30分钟 大肌群包括:胸.背.腿.臀: 小肌群包括:肩.二头肌.三头肌. ...

  8. LeetCode & linked list bug

    LeetCode & linked list bug add-two-numbers shit test /** * Definition for singly-linked list. * ...

  9. Flutter使用WebSockets

    文档 注意是WebSockets而不是socket.io install dependencies: web_socket_channel: demo import 'dart:convert'; i ...

  10. VAST算力增值效应,助力NGK全生态产业链!

    虽然比特币和区块链在2009年就诞生了,但它们对于一些人来说好像还是很遥远,归根结底还是由于数字货币始终未能在全球真正实现流通和支付功能.区块链1.0,以比特币为代表,实现了数字支付:区块链2.0,E ...