Java多线程:线程间通信之volatile与sychronized

由前文Java内存模型我们熟悉了Java的内存工作模式和线程间的交互规范,本篇从应用层面讲解Java线程间通信。

Java为线程间通信提供了三个相关的关键字volatile, synchronized和final。对于final,我们在博文Java中static关键字和final关键字中已经介绍。

  • 1. volatile
    • 1.1. 定义
    • 1.2. 机理
    • 1.3. 特性:不会被重排序
    • 1.4. 非原子性
  • 2. synchronized
    • 2.1. 定义
    • 2.2. synchronized与voliatile区别
    • 2.3. 注意
    • 2.4. synchronized的作用域
    • 2.5. synchronized应用
      • 2.5.1. synchronized方法
      • 2.5.2. synchronized代码块
      • 2.5.3. synchronized静态方法
      • 2.5.4. synchronized对象
  • 3. 参考文章

1. volatile

1.1. 定义

由volatile定义的变量其特殊性在于:

一个线程对变量的写一定对之后对这个变量的读的线程可见。

换言之

一个线程对volatile变量的读一定能看见它之前最后一个线程对这个变量的写。

1.2. 机理

volatile意味着可见性,在讲解volatile的机理前,我先给下面的这个例子:

package com.cielo.main;/*** Created by 63289 on 2017/3/31.*/
class MyThread extends Thread {private boolean isRunning = true;public boolean isRunning() {return isRunning;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run() {System.out.println("进入到run方法中了");while (isRunning == true) {}System.out.println("线程执行完成了");}
}
public class RunThread{public static void main(String[] args) {try {MyThread thread = new MyThread();thread.start();Thread.sleep(1000);thread.setRunning(false);} catch (InterruptedException e) {e.printStackTrace();}}
}

在这个例子中,主线程启动了子线程,子线程成功进入run方法,输出”进入到run方法中”,只有由于isRunning==true,无限循环。此时,sleep一秒后的主线程想要改变isRunning的值,它将isRunning变量读取到它的内存空间进行修改后,写入主内存,但由于子线程一直在私有栈中读取isRunning变量,没有在主内存中读取isRunning变量,因此不会退出循环。

如果我们把isRunning赋值行改为:

private volatile boolean isRunning = true;
将其用volatile修饰,则强制该变量从主内存中读取。

这样我们也就明白了volatile的实现机理,即:

  1. 当一个线程要使用volatile变量时,它会直接从主内存中读取,而不使用自己工作内存中的副本。

  2. 当一个线程对一个volatile变量写时,它会将变量的值刷新到共享内存(主内存)中。

1.3. 特性:不会被重排序

从Java内存模型一篇中,我们简单了解了重排序,这里不会被重排序主要指语句重排序。

我们考虑到下面这个例子,有A,B两个线程

线程A:加载配置文件,将配置元素初始化,之后标识初始化成功。

Map configOptions ;
char[] configText;volatile boolean initialized = false;//线程A首先从文件中读取配置信息,调用process...处理配置信息,处理完成了将initialized 设置为true
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfig(configText, configOptions);//负责将配置信息configOptions 成功初始化
initialized = true;
线程B:等待初始化标识为true,之后开始工作。while(!initialized)
{sleep();
}//使用配置信息干活
doSomethingWithConfig();

很简单的一个例子,在编译器中,如果进行重排序,则会有将initialized=true这一行先执行的可能,如果这件事发生的话,线程B就会先运行,进而使用了没有加载配置文件的Object。而如果initialized变量使用了volatile修饰,则编译器不会将该变量的相关代码进行重排序。(当然,这里的例子只是为了直观,实际情况编译器的重排序会更加复杂)

1.4. 非原子性

使用volatile时,我们要清楚,volatile是非原子性的。

原子性即是指,对于一个操作,其操作的内容只有全部执行/全不执行两个状态,不存在中间态。而volatile并不能锁定某组操作,防止其他线程的干扰,即没有规定原子性,因而volatile是非原子性的。或者说,volatile是非线程安全的。

综上,如果我们想要使用一个原子性的修饰符来控制操作,即在操作变量时锁定变量,我们就需要另一个修饰词synchronized。

2. synchronized

2.1. 定义

synchronized作用的代码范围对于不同线程是互斥的,并且线程在释放锁的时候会将共享变量的值刷新到共享内存中。

2.2. synchronized与voliatile区别

  1. 使用:voliatile 用于修饰变量,synchronized可以修饰对象,类,方法,代码块,语句。

  2. 原子性:voliatile只保证变量的可见性,不能用于同步变量,即不保证原子性,多线程并发访问voliatile修饰的变量时也不会产生阻塞。synchronized是原子性的,只有锁定了变量的线程才能进入临界区,从而保证临界区的所有语句全部执行。多线程并发访问sychronized修饰的变量会产生阻塞。

  3. 机理:

当线程对volatile变量读时,会把工作内存中值置为无效。当线程对sychronized变量读时,会在该线程锁定变量时把工作内存中值置为无效。

当线程对voliatile变量写时,会把值刷新到主内存中。当线程对sychronized变量写时,会在变量解锁时把值刷新到主内存中。

2.3. 注意

  1. 无论synchronized加在方法上还是对象上,其修饰的都是对象,而不是方法或者某个代码块代码语句。

  2. 每个对象只有一个锁与之相关联。

  3. 实现同步需要很大的系统开销来做控制,不要做无谓的锁定。

2.4. synchronized的作用域

synchronized的作用域只有两种。实际上,synchronized直接作用于内存中的一个内存块,因此,可以通过锁定内存块来锁定一个实例变量或者锁定一个静态区域。

  1. 某个对象实例内

synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法,如果对象有多个synchronized方法,则只要一个线程访问了任何一个synchronized方法,其他线程不能同时访问任何一个该对象的synchronized方法(synchronized作用于对象,且每个对象只有一个锁)。

显然,不同对象的synchronized方法则不会互相影响(synchronized作用于对象)。

  1. 某个类的范围

又或者说作用于静态方法/静态代码块。synchronized static aMethod(){}防止多个线程同时访问这个类中的synchronized static方法,它可以对类的所有实例对象起作用。

2.5. synchronized应用

2.5.1. synchronized方法

每个实例对应一个lock,线程获得该含有synchronized方法的实例的锁才可以执行,否则阻塞。方法一旦执行,则一直到方法返回才可以释放锁。此后被阻塞的线程才能获得该锁。对于一个实例,其声明为synchronized的方法显然只有一个能处于执行状态。从而避免了类访问变量的冲突。

synchronized同步的开销很大,如果synchronized作用于一个比较大的方法上,显然是不合算的。

2.5.2. synchronized代码块

synchronized代码块形式如下:synchronized (synchronizedObject){//Some thing}

代码块内部代码必须在获得synchronizedObject的锁时才能执行。需要重点说的是synchronized(this),这也是比较常用的代码块。

synchronized的效果类似于在方法前修饰,只是修饰的范围缩小成代码块。两个线程同时访问一个变量时,如果一个线程在执行synchronized的代码,那么该实例被锁定,另一个线程如果要访问该实例被synchronized作用的范围,则会被阻塞。

此外,如果不使用this作为锁,而是只是想让一段代码同步,可以临时创建如下锁:

    private byte[] lock=new byte[0];

从操作码上讲,创建一个长度为0的数组对象是最经济的,只需要3条操作码。

2.5.3. synchronized静态方法

synchronized修饰静态方法时或者在普通方法中以类为对象如下形式:

class StaticSynchronized{public void aMethod{synchronized (StaticSynchronized.class){//Some thing}}
}

为synchronized静态方法。

注意的是,对于同一个类,其static和实例方法如果都用synchronized修饰,其作用的必然不是同一个对象(显然)。

2.5.4. synchronized对象

比较简单粗暴的实现方式,直接把对象锁定,思路也很清晰。Java负责跟踪被加锁的对象,该锁定对象的线程每次给对象加锁时对象的计数器+1,每次解锁时计数器-1,如果对象的计数器为0,那么解除该线程的锁定。

3. 参考文章

如何使用 volatile, synchronized, final 进行线程间通信

JAVA多线程之volatile 与 synchronized 的比较

Java synchronized详解

转载于:https://www.cnblogs.com/cielosun/p/6650161.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

避免Java中的空指针异常

空指针异常是Java中最常见,最烦人的异常。 在这篇文章中,我想避免这种不希望的异常。 首先让我们创建引发空指针异常的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; …

权威发布 鸿蒙涅槃,鸿蒙涅槃 曾圣《鸿蒙》雕塑走进河北美院受追捧

原标题:鸿蒙涅槃 曾圣《鸿蒙》雕塑走进河北美院受追捧2019年10月20日上午10时曾圣《鸿蒙》雕塑作品展——暨河北美院首场新闻媒体发布会在河北美术学院雕塑院举办。河北美术学院校长甄忠义、河北美术学院党委书记高国忠、常务副校长张建敏、党委副书记甄心恒、副校长…

Rube GoldbergSpring整合

Spring Integration为集成系统所涉及的某些复杂性提供了非常好的抽象-Spring Integration从Integration的角度来看非常适合Facade的定义-简化了对复杂底层系统的访问。 为了说明这一点,请考虑一个简单的系统,该系统仅接收一条消息,然后将其发…

NSNotificationCenter

前几天面试富途证券&#xff0c;被问到添加通知的相关问题&#xff0c;当时有几个问题答错了&#xff0c;在此总结。 使用通知的要点 1.注册多少次&#xff0c;他的执行代码就会执行多少次 //1、注册多个通知 for (int i 0; i<3; i) {[[NSNotificationCenter defaultCenter]…

java ajax html复选框,HTMLjavaSkcriptCSSjQueryajax(九)(示例代码)

一、HTML1、单选按钮 Male Female2、复选框 I have a Car3、下拉列表 VoloFita如果设置某项选定直接 fem4、设置文本域 创建按钮 5、Runnob.com因为a 标签的target属性名是inframe_a的iframe框架&#xff0c;点击链接后页面会在iframe 中出现。6、脚本 JavaScript …

Spring和JSF集成:选择项目

使用JSF&#xff0c;要使用组合框&#xff0c;列表框和复选框&#xff0c;您需要了解javax.faces.model.SelectItem类。 SelectItem表示单个可选选项。 它既包含渲染所需的信息&#xff0c;又包含选择该项目时应绑定的值。 在大多数情况下&#xff0c; SelectItems是使用值和标…

apt-get update 问题 及gcc高亮

一 gcc高亮 gcc 高亮有好几种方法。 参考 http://www.cokco.cn/thread-39909-1-1.html 这个教程&#xff1a; &#xff08;1&#xff09; git clone https://github.com/chinaran/color-compile/ &#xff08;2&#xff09; cd color-compile make sudo make install&#xff0…

对于MVC中应用百度富文本编辑器问题的解决办法

&#xff11;.对于应用富文本编辑器&#xff50;&#xff4f;&#xff53;&#xff54;提交表单内容提示有危险的解决办法&#xff1a; [ValidateInput(false)] //文本编辑器的表单提交不用提示危险 [HttpPost] public ActionResult Create(FormCollection collection) 即在视图…

html5页面热力图几十万数据,基于百度地图的数据可视化,包括大量数据的标绘以及热力图的插入...

(function(global, factory) {typeof exports object && typeof module ! undefined ? module.exports factory() :typeof define function && define.amd ? define(factory) :(global.PointLine factory());}(this, (function() {use strict;/*** autho…

管理Jenkins作业配置

在JBoss工具和Developer Studio中&#xff0c;我们在Jenkins中管理许多构建作业。 实际上&#xff0c;对于3.2.x / 4.x和3.3.x / 5.x流&#xff0c;有195个以上的作业。 当我们开始建立明年的第一个里程碑时&#xff0c;我们将再产生40多个工作岗位。 这里是其中的一些&#xf…

Redis命令拾遗三(列表List类型)

本文版权归博客园和作者吴双本人共同所有。转载和爬虫请注明原文地址 Redis五种数据类型之列表类型Redis五种数据类型之列表类型。你可以存储一个有序的字符串列表一类数据。比如你想展示你所存储的所有文章中的前十条&#xff0c;当分页取下一页的时候&#xff0c;你也可以取接…

angular2安装笔记

主要摘自&#xff1a;http://www.runoob.com/angularjs2/angularjs2-typescript-setup.html http://blog.csdn.net/lgpwwa/article/details/51788035 开始尝试的时候npm install一直不能正常生成modules文件内的东东&#xff0c;后来试了多次才知道&#xff0c;大概是因为服务器…

计算机桌面上的声音图标没了怎么办,电脑声音图标不见了怎么办超详细教程

我们在用电脑或者平板看视频、听歌的时候&#xff0c;如果声音不合适&#xff0c;大了或小了就会调节音量&#xff0c;除了可以选择设备自带的外部音量大小调节按键外&#xff0c;还可以用自带的声音图标来调节&#xff0c;而屏幕上的声音图标是最方便的。如果屏幕上的声音图标…

XML解组基准:JAXB,STAx,Woodstox

介绍 上周末&#xff0c;我开始考虑如何以一种资源友好的方式处理大量XML数据。我要解决的主要问题是如何以块的形式处理大型XML文件&#xff0c;同时提供上游/下游系统&#xff0c;需要处理一些数据。 当然&#xff0c;我已经使用JAXB技术已有几年了。 使用JAXB的主要优点是可…

ArcGIS空间分析工具

1. 3D分析 1.1. 3D Features toolset 工具 工具 描述 3D Features toolset &#xff08;3D 要素工具集&#xff09; Add Z Information 添加 Z 信息 添加关于具有 Z 值的要素类中的要素的高程属性的信息。 Buffer 3D 3D 缓冲 围绕点或线创建三维缓冲区以生成球形或圆柱形的多面…

SHELL编程中如果路径名遇到括号

linux中&#xff0c;如果文件名中带括号&#xff0c;应先对括号处理&#xff0c;在“(”和“&#xff09;”前加上“\”。Bracket_Handle给出了处理方法&#xff1a;sub Bracket_Handle { my ($tmp_name) _; $tmp_name ~ s/\(/\\\(/g; # ( > \( $tmp_name ~ s/\)/…

计算机科学家和心理学家合作,生物智能与人工智能之间的合作

李飞飞带领的斯坦福“以人为本AI研究院”发表文章&#xff0c;探讨人工智能、神经科学、心理学和认知科学&#xff0c;以及数学、物理和社会科学等学科过去是怎样、以及未来将如何共同合作&#xff0c;追求对理解和创造智能系统的探索。最初的类人智能出现在几百万年前的非洲大…

JavaOne 2012 – 2400小时! 一些建议

您可能已经看到JavaOne 2012 Content Catalog在线。 计划委员会经过数周的艰苦工作&#xff0c;对每个提案进行了分类&#xff0c;审查&#xff0c;评分和讨论&#xff0c;我们终于为您设置了&#xff08;希望如此&#xff09;有趣的组合。 整整105天或2400个小时&#xff0c;我…

C语言

自定义函数 &#xff08;1&#xff09;在函数使用之前定义函数 &#xff08;2&#xff09;先声明这个函数&#xff0c;然后使用&#xff0c;使用的代码后面定义这个函数 include <stdio.h> void syahello(){ println("helloo"); }int main(){ sayhello(); // 调…

【Centos】yum 安装mariaDB

[dream361za ~]$ sudo yum search mariadb #查找需安装的包 mariadb-libs.x86_64 : The shared libraries required for MariaDB/MySQL clients #mariadb客户端 mariadb-server.x86_64 : The MariaDB server and related files #mariadb服务 [dream361za ~]$ sudo yum instal…