`
yuwenlin2008
  • 浏览: 126537 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java并发编程:synchronized&volatile

阅读更多
根据前面讲的Java内存模型,已经接触不少synchronized,而且它非常强大,能解决大部分的并发问题,今天我们一起来学习它吧。
以下是本文包含的知识点:
1.Java的线程安全
2.synchronized的用法
3.synchronized的实现原理
4.volatile关键字
 
一、Java的线程安全
我们这里讨论的线程安全,限定于多个线程之间存在共享数据访问的这个前提下。如果一段代码根本不会和其它线程共享数据,那么也不存在线程安全的问题。
那我们应该如何实现线程安全呢?
互斥同步是一种常见的并发正确性保障手段,同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。而互斥是实现同步的一种手段。
在Java中最基本的互斥同步手段就是synchronized关键字。还有一种是重入锁(ReentrantLock),后面会讲到。
 
二、synchronized的用法
可以参考之前的文章Thread类的使用,线程同步一节有讲到
1.作用于代码块上,只同步这一段代码:
synchronize(this){//this指锁定当前对象
     num++;
     System. out.println(name + ", 你是第" + num + "个使用timer的线程" );
}
 2.放在方法声明中,表明整个方法为同步方法:
public synchronized  void  add(String name) {//还可以修饰static方法
    num++;
    System. out.println(name + ", 你是第" + num + "个使用timer的线程" );
}
 有几点需要注意的地方:
1.当一个线程正在访问一个对象的synchronized方法时,其它线程不能访问该对象的其它synchronized方法。因为该对象的锁还未被释放,其它线程拿不到。
2.当一个线程正在访问一个对象的synchronized方法时,其它线程可以访问该对象的其它非synchronized方法。因为非synchronized方法,不需要锁。
3.当一个线程正在访问一个对象的synchronized方法时,其它线程可以访问其它对象的synchronized方法或非synchronized方法。因为锁的对象不一样。
 
另外,每个类也有自己的锁,被synchronized修饰的static方法,类锁与对象的锁也不会发生互斥。看下面代码:
public synchronized void test1(){//对象锁
     //
}
public synchronized static void test2(){//类锁
     //
}
 当多线程同时访问test1()和test2()时,可以并发访问,不会发生互斥。因为锁的对象不一样,一个是类,一个是对象。
 
三、synchronized的实现原理
synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要一个reference类型参数来指明需要锁定和解锁的对象。如果在程序中明确指定了对象参数,那就是这个对象的reference,如果没有明确指定,那根据synchronized修饰的是实例方法还是静态方法,去取对应的对象实例或Class对象来作为锁对象。
看下面这段代码:
package com.yuwl.thread.demo;

public class TestSynchronized {

     public void test1(){
          
     }
     
     public void test2(){
           synchronized( this){
          }
     }
     
     public synchronized void test3(){
          
     }
     
}
 对其反编译的字节码为:

从反编译的字节码可以看出,加了synchronized的代码块多了两个指令。
 
根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁。如果已经拿到对象的锁,则把计数器加1,相应的在执行monitorexit指令时会将计数器减1,当计算器为0时,锁就被释放了。如果获取对象失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放。对于synchronized方法,执行中的线程会识别该方法的method_info结构是否有ACC_SYNCHRONIZED标记,如果有会自动获取对象的锁,调用方法,最后释放锁。
 
synchronized还有一个重要特性——可重入性,不会自己把自己锁死。解释下,比喻一个线程执行同步方法1,而同步方法1又要调用同步方法2,这种情况是可重入的。
 
synchronized对异常的处理,如果同步块有异常发生,线程会自动释放锁。
 
四、volatile关键字
直接先举一个例子:
public class MyThread28 extends Thread
{
    private boolean isRunning = true;

    public boolean isRunning()
    {
        return isRunning;
    }

    public void setRunning(boolean isRunning)
    {
        this.isRunning = isRunning;
    }
    
    public void run()
    {
        System.out.println("进入run了");
        while (isRunning == true){}
        System.out.println("线程被停止了");
    }
}
public static void main(String[] args)
{
    try
    {
        MyThread28 mt = new MyThread28();
        mt.start();
        Thread.sleep(1000);
        mt.setRunning(false);
        System.out.println("已赋值为false");
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}
看一下运行结果:
进入run了
已赋值为false

也许这个结果有点奇怪,明明isRunning已经设置为false了, 线程还没停止呢?

这就要从Java内存模型(JMM)说起,这里先简单讲,虚拟机那块会详细讲的。根据JMM,Java中有一块主内存,不同的线程有自己的工作内存,同一个变量值在主内存中有一份,如果线程用到了这个变量的话,自己的工作内存中有一份一模一样的拷贝。每次进入线程从主内存中拿到变量值,每次执行完线程将变量从工作内存同步回主内存中。

出现打印结果现象的原因就是主内存和工作内存中数据的不同步造成的。因为执行run()方法的时候拿到一个主内存isRunning的拷贝,而设置isRunning是在main函数中做的,换句话说 ,设置的isRunning设置的是主内存中的isRunning,更新了主内存的isRunning,线程工作内存中的isRunning没有更新,当然一直死循环了,因为对于线程来说,它的isRunning依然是true。

解决这个问题很简单,给isRunning关键字加上volatile。加上了volatile的意思是,每次读取isRunning的值的时候,都先从主内存中把isRunning同步到线程的工作内存中,再当前时刻最新的isRunning。看一下给isRunning加了volatile关键字的运行效果:   
进入run了
已赋值为false
线程被停止了

看到这下线程停止了,因为从主内存中读取了最新的isRunning值,线程工作内存中的isRunning变成了false,自然while循环就结束了。

volatile的作用就是这样,被volatile修饰的变量,保证了每次读取到的都是最新的那个值。线程安全围绕的是可见性原子性这两个特性展开的,volatile解决的是变量在多个线程之间的可见性,但是无法保证原子性

多提一句,synchronized除了保障了原子性外,其实也保障了可见性。因为synchronized无论是同步的方法还是同步的代码块,都会先把主内存的数据拷贝到工作内存中,同步代码块结束,会把工作内存中的数据更新到主内存中,这样主内存中的数据一定是最新的。  
参考
《深入Java 虚拟机》
http://www.cnblogs.com/xrq730/p/4853578.html
  • 大小: 13.8 KB
0
0
分享到:
评论

相关推荐

    并发编程之JMM&synchronized&volatile详解.pdf

    【并发编程】是计算机科学中的一个重要概念,尤其是在多任务处理和高效系统设计中不可或缺。...因此,深入学习并发编程的关键概念以及它们在现代计算机硬件上的实现,是成为高效Java开发者的必备技能。

    Java并发编程:volatile关键字解析

    ### Java并发编程:volatile关键字解析 #### 一、内存模型的相关概念 在深入了解`volatile`关键字之前,我们首先需要理解计算机内存模型的一些基本概念。在现代计算机系统中,CPU为了提高执行效率,会将频繁访问的...

    Java并发编程:设计原则与模式(第二版)-3

    《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java多线程编程技术的权威著作。这本书详细阐述了在Java平台中进行高效并发处理的关键概念、设计原则和实用模式。以下是对该书内容的一些核心知识点的概述...

    java并发编程:设计原则与模式.rar

    《Java并发编程:设计原则与模式》是一本深入探讨Java多线程编程的书籍,它涵盖了并发编程中的关键概念、原则和模式。在Java中,并发处理是优化应用程序性能、提高资源利用率的重要手段,尤其在现代多核处理器的环境...

    Java并发编程:设计原则与模式(第二版).rar

    《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java平台上的多线程和并发编程的著作。本书旨在帮助开发者理解和掌握在Java环境中进行高效并发处理的关键技术与设计模式。以下是对该书内容的一些核心知识...

    Java并发编程:设计原则与模式2中文版

    《Java并发编程:设计原则与模式2中文版》是一本深度探讨Java开发中并发编程的专著,旨在帮助开发者理解和掌握在多线程环境下编写高效、安全、可维护的代码。这本书涵盖了Java并发编程的核心概念、最佳实践以及常用...

    Java并发编程:设计原则与模式(第二版)

    java并发方面的两大名著之一。读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位...

    《Java并发编程:设计原则与模式(第二版)》

    《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java多线程编程技术的权威著作。这本书详细阐述了如何在Java环境中高效、安全地进行并发编程,涵盖了多线程设计的关键原则和常见模式。对于Java开发者来说...

    Java 并发编程:设计原则与模式

    本资料“Java并发编程:设计原则与模式”深入探讨了这些关键主题。 首先,我们需要理解Java并发编程的基础概念。Java中的并发是通过线程实现的,线程是程序执行的最小单位。Java提供了多种创建和管理线程的方法,如...

    java并发编程实战源码,java并发编程实战pdf,Java

    《Java并发编程实战》是Java并发编程领域的一本经典著作,它深入浅出地介绍了如何在Java平台上进行高效的多线程编程。这本书的源码提供了丰富的示例,可以帮助读者更好地理解书中的理论知识并将其应用到实际项目中。...

    一本经典的多线程书籍 Java并发编程 设计原则与模式 第二版 (英文原版)

    《Java并发编程 设计原则与模式 第二版》是一本深受程序员喜爱的经典书籍,由Addison Wesley出版。这本书深入探讨了Java平台上的多线程编程技术,为开发者提供了丰富的设计原则和模式,帮助他们理解和解决并发环境中...

    Java并发编程:设计原则与模式(Concurrent.Programming.in.Java)(中英版)

    《Java并发编程:设计原则与模式》是一本深入探讨Java多线程编程的权威书籍,由Doug Lea撰写,第二版全面涵盖了Java并发处理的各个方面。这本书不仅提供了丰富的理论知识,还介绍了实战中的设计原则和模式,对于Java...

    Java并发编程的艺术&源码

    《Java并发编程的艺术》是一本深入探讨Java平台上的并发编程技术的专业书籍,它结合理论与实践,详细解析了Java并发编程中的各种概念、工具和最佳实践。这本书的源码提供了丰富的示例,帮助读者更好地理解和应用书中...

    并发编程一之synchronized和volatile.rar

    在并发编程领域,正确理解和使用同步机制至关重要,其中`synchronized`和`volatile`是Java中两个关键的并发控制工具。本资源"并发编程一之synchronized和volatile.rar"提供了多个示例代码(demo1到demo12),帮助...

    java并发编程:设计与模式

    Java并发编程是计算机科学中一个复杂而重要的领域,主要关注如何在Java程序中合理地使用多线程以及同步机制来提高程序执行的效率和响应性。在Java中,并发编程不仅仅是关于多线程,它还涉及到内存管理、线程调度、...

    java 并发编程的艺术pdf清晰完整版 源码

    《Java并发编程的艺术》这本书是Java开发者深入理解并发编程的重要参考书籍。这本书全面地介绍了Java平台上的并发和多线程编程技术,旨在帮助开发者解决在实际工作中遇到的并发问题,提高程序的性能和可伸缩性。 ...

    Java并发编程:设计原则与模式(第二版)_阅读密码www.zasp.net_仅提供试看如需要请购买原版书

    Java并发编程是软件开发中的一个核心领域,尤其是在服务器端应用和多核处理器系统中,它的重要性日益凸显。《Java并发编程:设计原则与模式(第二版)》这本书深入探讨了这个主题,旨在帮助开发者理解并有效地利用...

    《java 并发编程实战高清PDF版》

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南。这本书旨在帮助开发者理解和掌握在Java环境中创建高效、可扩展且可靠的多线程应用程序的关键技术和实践。它涵盖了从基本概念到高级主题的广泛内容,...

    Java并发编程_设计原则和模式(CHM)

    Java并发编程是软件开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。本资源"Java并发编程_设计原则和模式(CHM)"聚焦于Java语言在并发环境下的编程技巧、...

    java并发编程2

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。以下是对标题和描述中所提及的几个知识点的详细解释: 1. **线程与并发** - **线程*...

Global site tag (gtag.js) - Google Analytics