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 * */ ...
随机推荐
- 南大算法设计与分析课程OJ答案代码(1)中位数附近2k+1个数、任意两数之和是否等于给定数
问题1 用来测试的,就不说了 问题2:中位数附近2k+1个数 给出一串整型数 a1,a2,...,an 以及一个较小的常数 k,找出这串数的中位数 m 和最接近 m 的小于等于 m 的 k 个数,以及 ...
- Spark学习(一)
一.启动Spark 1.standalone模式 方法一: (1)启动主节点 ./sbin/start-master.sh 访问http://127.0.0.1:8080,得到如下页面 注意标记到的链 ...
- [转]Ubuntu18.04下使用Docker Registry快速搭建私有镜像仓库
本文转自:https://blog.csdn.net/BigData_Mining/article/details/88233015 1.背景 在 Docker 中,当我们执行 docker pull ...
- C# 中 FindControl 方法及使用
FindControl 的使用方法 FindControl (String id): 在页命名容器中搜索带指定标识符的服务器控件.(有点类似javascript中的getElementById(st ...
- 推送GitHub报错 fatal: Out of memory, malloc failed 解决办法
现象: 推送GitHub时,出现如下报错 fatal: Out of memory, malloc failed (tried to allocate XXXXXX bytes)error: fail ...
- 1. 七种join的sql编写
一.join图 二.sql演示 a.创建演示表及数据 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ----------------------- ...
- 7个拒绝使用TypeScript的借口
译者按: TypeScript 学习成本不高,项目切换成本不低,不过还是值得试一试的! 原文:7 bad excuses for not using TypeScript 译者: Fundebug 为 ...
- O(n*logn)级别的算法之一(归并排序及其优化)
原理: 设两个有序的子序列(相当于输入序列)放在同一序列中相邻的位置上:array[low..m],array[m + 1..high],先将它们合并到一个局部的暂存序列 temp (相当于输出序列) ...
- K8S 调度器,预选策略,优选函数
Kubernetes Scheduler 提供的调度流程分三步: 预选策略(predicate) 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择. 优 ...
- 16.Odoo产品分析 (二) – 商业板块(9) – 网站生成器(1)
查看Odoo产品分析系列--目录 安装"电子商务"模块时,该模块会自动安装,但网站生成器是电子商务的前提,因此,先分析该模块,在下面就是对电子商务模块的分析. 1. 编辑网站 安装 ...