前言

线程间的通信主要通过共享对字段的访问和对象引用字段的引用,可能会产生两种错误,线程干扰和内存一致性错误。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)的更多相关文章

  1. 菜鸡的Java笔记 - java 线程的同步与死锁 (同步 synchronization,死锁 deadlock)

    线程的同步与死锁 (同步 synchronization,死锁 deadlock)        多线程的操作方法            1.线程同步的产生与解决        2.死锁的问题     ...

  2. 监视锁——Java同步的基本思想

    翻译人员: 铁锚翻译时间: 2013年11月13日原文链接: Monitors – The Basic Idea of Java synchronization如果你上过操作系统课程,你就知道监视锁( ...

  3. 【线程系列四】[转]监听器-java同步的基本思想

    转自:http://ifeve.com/think-in-java-monitor/ 如果你在大学学习过操作系统,你可能还记得监听器在操作系统中是很重要的概念.同样监听器在java同步机制中也有使用, ...

  4. 锁——Java同步的基本思想

    翻译人员: 铁锚 翻译时间: 2013年11月13日 原文链接:  Monitors – The Basic Idea of Java synchronization 如果你上过操作系统课程,你就知道 ...

  5. Java同步块

    原文:http://ifeve.com/synchronized-blocks/ Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本 ...

  6. 探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现]

    探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现] https://www.ibm.com/developerworks/cn/java/j-lo-syn ...

  7. Java同步块(synchronized block)使用详解

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

  8. 转:Java同步synchronized使用

    原文链接 作者:Jakob Jenkov Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(s ...

  9. java同步synchronized

    java同步synchronized volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性. 看下面的这段代码: /** * * @author InJavaWeTrust * */ ...

随机推荐

  1. 分布式系统监视zabbix讲解二之邮件报警通知--技术流ken

    概述 在上一篇博客<分布式系统监视zabbix讲解一技术流ken>中已经详细讲解了如何安装zabbix,本篇博客将详细讲解如何使用zabbix监控另外一台主机,并实现email报警通知机制 ...

  2. c# nginx 配置

    listen ; #端口 server_name localhost; #域名可以有多个 用空格隔开 #charset koi8-r; #access_log logs/host.access.log ...

  3. SQL 用于各种数据库的数据类型(转载) sqlserver 数据类型 取值范围 长度

    SQL 用于各种数据库的数据类型 来源 http://www.runoob.com/sql/sql-datatypes.html    面向数据库编程中,数据类型的取值范围.长度,可能是需要经常查看的 ...

  4. [leetcode]984. 不含 AAA 或 BBB 的字符串

    给定两个整数 A 和 B,返回任意字符串 S,要求满足: S 的长度为 A + B,且正好包含 A 个 'a' 字母与 B 个 'b' 字母: 子串 'aaa' 没有出现在 S 中: 子串 'bbb' ...

  5. Reinforcement Learning: An Introduction读书笔记(1)--Introduction

      > 目  录 <   learning & intelligence 的基本思想 RL的定义.特点.四要素 与其他learning methods.evolutionary m ...

  6. JavaAndroid项目配置文件笔记

    配置文件AndroidManifest.xml如下: <?xml version="1.0" encoding="utf-8"?> <!-- ...

  7. C#中try catch finally的执行顺序

    1.首先明确一点,就是不管怎样,finally一定会执行,即使程序有异常,并且在catch中thorw 了 ,finally还是会被执行. 2.当try和catch中有return时,finally仍 ...

  8. 淘宝cnpm

    npm install -g cnpm --registry=https://registry.npm.taobao.org

  9. spring boot maven打包可运行jar包

    普通打包之后在程序目录运行,或者编写bat运行时会提示“没有主清单属性”,这是因为并没有找到main()方法,需要我们指明告诉java程序 我bat中的代码 @echo off title mytit ...

  10. ThinkPhp框架对“数据库”的基本操作

    框架有时会用到数据库的内容,在"ThinkPhp框架知识"的那篇随笔中提到过,现在这篇随笔详细的描述下. 数据库的操作,无疑就是连接数据库,然后对数据库中的表进行各种查询,然后就是 ...