资讯专栏INFORMATION COLUMN

多线程学习笔记(1):volatile和synchronized

jk_v1 / 2042人阅读

摘要:今天开始整理学习多线程的知识,谈谈最重要的两个关键字和。但是这样一个过程比较慢,在使用多线程的时候就会出现问题。有序性有序性是指多线程执行结果的正确性。这种机制在多线程中会出现问题,因此可以通过来禁止重排。

今天开始整理学习多线程的知识,谈谈最重要的两个关键字:volatile和synchronized。

一、三个特性 1、原子性

所谓原子性操作就是指这些操作是不可中断的,要么执行过程中不被中断,要么不做。在Java中对基本数据类型的读取和赋值操作是原子性操作,比如i++就不是原子性操作,分为三步:读取i,i++,写回。

2、可见性

这个特性可以通过Java的内存模型来理解。
链接描述

Java的内存模型如上图所示,可以看到,一个线程对变量进行操作时,首先需要将变量从主存拷贝一个副本到工作内存中,操作完之后再给工作内存中的变量赋值,再传回主存。但是这样一个过程比较慢,在使用多线程的时候就会出现问题。

而所谓的内存可见性,就是在使用volatile关键字之后,对变量的修改会立即刷回主存。

3、有序性

有序性是指多线程执行结果的正确性。这里介绍一下指令重排的概念。

指令重排:这一机制是指JMM允许对指令进行重排,但是这种重排不会影响程序的结果。比如a++; b++;两条指令之间没有联系,因此可以调换顺序。
这种机制在多线程中会出现问题,因此可以通过volatile来禁止重排。

二、volatile 1、volatile关键字的特性:

(1)保证了不同线程对被修饰变量操作的内存可见性
(2)禁止了指令的重排序

从内存的语义上看,写volatile变量时,会将更新的值直接写回到主存;读volatile变量时,会直接从主存中读取变量。

2、volatile关键字的特点

volatile关键字可以保证有序性可见性,但是无法保证原子性

举一个例子,现在内存中有一个count为0,现在A进程和B进程想对count做加一操作,首先A和B都将count取出,A和B的工作内存中的count值都为0,此时A做加一操作,并写回主存,而B也只会对0做加一操作,因为B已经读完了,他并不知道主存中的count已经被修改过了,因此可见无法保证原子性。

3、volatile的底层实现

volatile主要是通过lock指令前缀来实现的:
(1)重排序时不能把后面的指令重排到前面
(2)使本CPU的cache写入主存
(3)写入时,其他CPU的cache无效,也就是直接从主存中读取,对外部可见

三、synchronized关键字 1、概念

(1)一个非常直观的解释是:
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
(2)官方的解释是:
Java中的synchronized,通过使用内置锁,来实现对变量的同步操作,进而实现了对变量操作的原子性和其他线程对变量的可见性,从而确保了并发情况下的线程安全

2、特点

比起volatile关键字,synchronized关键字不仅能够保证可见性,还可以保持原子性。

3、实现

synchronized关键字的原理,就是通过锁,线程拿到锁之后,其他线程就会阻塞等待。

在Java中每一个对象都有一个与之关联的锁,称为内置锁
当我们使用synchronized修饰非静态方法时,用的是调用该方法的实例的内置锁,也就是this;
当我们使用synchronized修饰静态方法时,用的是调用该方法的所在的类对象的内置锁;
更多时候,我们使用的是synchronized代码块,我们经常用的是synchronized(this),也就是把对象实例作为锁。如下

    public void process() {
        doProcess();
        synchronized (this) {
            count ++;
        }
    }
4、重入问题
public class Widget {
    public synchronized void doSomething() {
        ...
    }
}

public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();
    }
}

值得注意的是,在这个程序中,两个doSomething函数都有synchronized关键字,那么在执行过程中是否会发生死锁呢,答案是否定的,因为获得锁的是线程,因此这两个函数都用的是同一个锁,因此又称为可重入锁

参考:
https://hzy38324.gitbooks.io/... 《Java并发编程实践》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75131.html

相关文章

  • <jdk7学习笔记>读书笔记-线程

    摘要:如果把注释去掉,则在所以非线程都结束时,自动终止。默认所有从线程产生的线程也是线程。停止线程线程完成方法后,就进入状态。被标示为的区块会被监控,任何线程要执行区块都必须先获得指定的对象锁定。 Tread和Runnable 定义线程 实现Runnable接口,重写run()方法 继承Thread类,重写run()方法 启动线程 Runnable tortoise=ne...

    woshicixide 评论0 收藏0
  • Java线程volatile

    摘要:多线程是一个庞大的知识体系,这里对其中的进行一个总结,理清他的来龙去脉。替换重量级锁在中又称为重量级锁,能够保重的几大特性一致性,原子性,可见性。 Java多线程是一个庞大的知识体系,这里对其中的volatile进行一个总结,理清他的来龙去脉。 CPU缓存 要搞懂volatile,首先得了解CPU在运行过程中的存储是如何处理的,其结构如图 showImg(https://segment...

    ZweiZhao 评论0 收藏0
  • 【Java并发编程的艺术】第二章读书笔记synchronized关键字

    摘要:在之前的文章中学习了关键字,可以保证变量在线程间的可见性,但他不能真正的保证线程安全。线程执行到指令时,将会尝试获取对象所对应的的所有权,即尝试获得对象的锁。从可见性上来说,线程通过持有锁的方式获取变量的最新值。 在之前的文章中学习了volatile关键字,volatile可以保证变量在线程间的可见性,但他不能真正的保证线程安全。 /** * @author cenkailun *...

    GT 评论0 收藏0
  • [学习笔记-Java集合-7] Map - ConcurrentHashMap 源码分析(一)

    摘要:简介是的线程安全版本,内部也是使用数组链表红黑树的结构来存储元素。相比于同样线程安全的来说,效率等各方面都有极大地提高。中的关键字,内部实现为监视器锁,主要是通过对象监视器在对象头中的字段来表明的。 简介 ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组 + 链表 + 红黑树)的结构来存储元素。 相比于同样线程安全的HashTable来说,效率等各方...

    SoapEye 评论0 收藏0
  • Java线程笔记(一):JMM与基础关键字

    摘要:当线程执行完后进入状态,表示线程执行结束。其中和表示两个线程。但要注意,让出并不表示当前线程不执行了。关键字其作用是防止指令重排和使线程对一个对象的修改令其他线程可见。 JMM特性一览 Java Memory Model的关键技术点都是围绕着多线程的原子性、可见性和有序性来建立的。因此我们首先需要来了解这些概念。 原子性(Atomicity) 原子性是指一个操作是不可中断的。即使是在多...

    cyixlq 评论0 收藏0

发表评论

0条评论

jk_v1

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<