Java同步(Synchronization)
前言
线程间的通信主要通过共享对字段的访问和对象引用字段的引用,可能会产生两种错误,线程干扰和内存一致性错误。Java的同步就是防止这些错误,但当多个线程访问同一资源会导致线程执行缓慢,甚至暂停执行。
线程干扰(Thread Interference)
例子
class Counter {
private int c = 0; public void increment() {
c++;
} public void decrement() {
c--;
} public int value() {
return c;
} }
如果现在有两个线程,线程A执行increment,线程B执行decrement,它们都使用了相同的变量c,这时会发生干扰。即便是执行非常简单的语句,但简单语句也可以转化为Java虚拟机的多个步骤,例如c++可以分解为三个步骤:
1、检索c的当前值。
2、将检索值加1。
3、将值存储回c中。
PS:c—也可以分解为相同步骤,只是第二步是减1。
假如线程A和线程B几乎同时调用,c的初始值为0,则它们交错的步骤可能是以下顺序:
线程A:检索c。
线程B:检索c。
线程A:增加检索值;结果是1。
线程B:减少检索值;结果是-1。
线程A:将结果存储在c中; c现在是1。
线程B:将结果存储在c中; c现在是-1。
线程A的结果丢失了,被线程B覆盖了。这种交错只是一种可能性,也可能是B的结果丢失,也可能不会出错。也就是因为顺序是不可预测的,所以线程干扰的错误可能难以发现和修复。
内存一致性错误
不同线程看到的内存中的同一份数据却有不同的“视图”,原因很复杂,作为程序员最需要做的是处理好“happens-before”的关系,避免问题。
同步方法
Java提供了两种基本的同步方法:同步方法和同步语句。同步的行为是依赖锁来构建的,每一个对象都有与之相关的固定锁,想要独占访问对象的线程必须先获取对象的锁(拿不到阻塞线程),完成后释放锁,同时与请求同一锁的线程建立一个happens-before关系。
同步方法的作用:
1、当一个线程正在执行一个对象的同步方法时,所有其他调用该对象的同步方法的线程将被阻塞,直到第一个线程执行完成。
2、当一个同步方法退出后,会自动建立与后续调用的同步方法(相同对象)的一个happens-before关系,保证所有线程对对象状态的修改可见。
注意:
1、在构造函数中使用synchronized关键字是语法错误。同步构造函数没有意义,因为只有创建对象的线程在构建时才能访问它。
2、如果一个对象对多个线程可见,则通过同步方法完成对该对象变量的所有读取或写入操作,不然还是会出现线程干扰和内存一致性错误。
public class SynchronizedCounter {
private int c = 0; public synchronized void increment() {
c++;
} public synchronized void decrement() {
c--;
} public synchronized int value() {
return c;
}
}
同步语句
另一种创建同步代码的方法,与同步方法不同,同步语句必须指定提供内部锁的对象。同步语句有利于通过细粒度同步来提高并发性。
例如,假设类MsLunch具有两个实例字段c1和c2,它们从不一起使用(很重要的前提条件!)。这些字段的所有更新都必须同步,但没有理由阻止c1的更新与c2的更新交错,这样做会创建不必要的阻塞来降低并发性。我们不使用同步方法,而是创建两个对象来提供锁。
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object(); public void inc1() {
synchronized(lock1) {
c1++;
}
} public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
volatile关键字
原子操作:一个原子操作,要么发生,要么不发生。比如 c=0;(非long和double类型) 这个操作是执行了就会发生。再比如:c++;可分割成三个操作步骤,执行时可能会丢失某些步骤,就不是一个原子操作。非原子操作都会存在线程安全问题,同步方法和同步语句可以让它变成一个原子操作。
volatile变量是一种稍弱的同步机制,所有声明为volatile的变量(包括long和double),读取和写入都是原子的。
volatile变量特性:
1、当一个线程读取一个volatile变量时,它看到总是最新的值。
2、原子动作不能交错,使用volatile变量不用担心线程干扰。
PS:java.util.concurrent包下提供了一些原子类。
参考文献
https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
Java同步(Synchronization)的更多相关文章
- 菜鸡的Java笔记 - java 线程的同步与死锁 (同步 synchronization,死锁 deadlock)
线程的同步与死锁 (同步 synchronization,死锁 deadlock) 多线程的操作方法 1.线程同步的产生与解决 2.死锁的问题 ...
- 监视锁——Java同步的基本思想
翻译人员: 铁锚翻译时间: 2013年11月13日原文链接: Monitors – The Basic Idea of Java synchronization如果你上过操作系统课程,你就知道监视锁( ...
- 【线程系列四】[转]监听器-java同步的基本思想
转自:http://ifeve.com/think-in-java-monitor/ 如果你在大学学习过操作系统,你可能还记得监听器在操作系统中是很重要的概念.同样监听器在java同步机制中也有使用, ...
- 锁——Java同步的基本思想
翻译人员: 铁锚 翻译时间: 2013年11月13日 原文链接: Monitors – The Basic Idea of Java synchronization 如果你上过操作系统课程,你就知道 ...
- Java同步块
原文:http://ifeve.com/synchronized-blocks/ Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本 ...
- 探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现]
探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现] https://www.ibm.com/developerworks/cn/java/j-lo-syn ...
- Java同步块(synchronized block)使用详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- 转:Java同步synchronized使用
原文链接 作者:Jakob Jenkov Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(s ...
- java同步synchronized
java同步synchronized volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性. 看下面的这段代码: /** * * @author InJavaWeTrust * */ ...
随机推荐
- JQuery官方学习资料(译):$( document ).ready()
一个页面直到document是”ready“才能被安全的操作,Jquery为你检查这种状态.代码包含在$( document ).ready()的内部将会仅仅运行一次在页面Document ...
- [android] 保存文件到手机内存
/*****************2016年5月4日 更新*******************************/ 知乎:Android 没有沙盒保护机制吗,WhatsApp 信息为何可被随 ...
- [android] 采用服务录制电话&服务的生命周期
根据上一节代码里,加入一个录音功能,上传到服务器,就能实现一个录制器 当手机处于通话状态时,开启录音机 获取MediaRecorder对象,通过new出来 调用MediaRecorder对象的setA ...
- 阿里分布式服务框架Dubbo的架构总结
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合).从服务模型的角度来看,Dubbo采用的是一种非常简单的模 ...
- 【Java每日一题】20170329
20170328问题解析请点击今日问题下方的“[Java每日一题]20170329”查看(问题解析在公众号首发,公众号ID:weknow619) package Mar2017; public cla ...
- linux /mac 下 go环境变量配置
安装了go语言之后,还要设置路径,如果不设置路径,则执行 go 的时候会提示 go: command not found,提示的意思是没有这个命令行.这个是因为还没有设置PATH路径. 设置路径的方式 ...
- 如何通过setTimeout理解JS运行机制详解
setTimeout()函数:用来指定某个函数或某段代码在多少毫秒之后执行.它返回一个整数,表示定时器timer的编号,可以用来取消该定时器. 例子 ? 1 2 3 4 5 console.log(1 ...
- MySQL 内置函数
CHAR_LENGTH(str) 返回值为字符串str 的长度,长度的单位为字符.一个多字节字符算作一个单字符. +------------------------+ | CHAR_LENGTH('k ...
- [笔记]JavaScript 秘密花园
1.hasOwnProperty相关 为了判断一个对象是否包含自定义属性而不是原型链上的属性,我们需要使用继承自 Object.prototype 的 hasOwnProperty方法.hasOwnP ...
- HDU 2586 How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...