Java同步与异步(转载)

Java同步与异步java

1、关键字:

thread(线程)、thread-safe(线程安全)、intercurrent(并发的)

synchronized(同步的)、asynchronized(异步的)、

volatile(易变的)、atomic(原子的)、share(共享)

2、总结背景:

一次读写共享文件编写,嚯,好家伙,居然揪出这些零碎而又是一路的知识点。因而乎,Google和翻阅了《Java参考大全》、《Effective Java Second Edition》,特此总结一下供往后工做学习参考。

3、概念:

一、 何时必须同步?什么叫同步?如何同步?

       要跨线程维护正确的可见性,只要在几个线程之间共享非 final 变量,就必须使用 synchronized(或 volatile)以确保一个线程能够看见另外一个线程作的更改。

为了在线程之间进行可靠的通讯,也为了互斥访问,同步是必须的。这归因于java语言规范的内存模型,它规定了:一个线程所作的变化什么时候以及如何变成对其它线程可见。

由于多线程将异步行为引进程序,因此在须要同步时,必须有一种方法强制进行。例如:若是2个线程想要通讯而且要共享一个复杂的数据结构,如链表,此时须要确保它们互不冲突,也就是必须阻止B线程在A线程读数据的过程当中向链表里面写数据(A得到了锁,B必须等A释放了该锁)。

为了达到这个目的,java在一个旧的的进程同步模型——监控器(Monitor)的基础上实现了一个巧妙的方案:监控器是一个控制机制,能够认为是一个很小的、只能容纳一个线程的盒子,一旦一个线程进入监控器,其它的线程必须等待,直到那个线程退出监控为止。经过这种方式,一个监控器能够保证共享资源在同一时刻只可被一个线程使用。这种方式称之为同步。(一旦一个线程进入一个实例的任何同步方法,别的线程将不能进入该同一实例的其它同步方法,可是该实例的非同步方法仍然可以被调用)。

错误的理解:同步嘛,就是几个线程能够同时进行访问。

同步和多线程关系:没多线程环境就不须要同步;有多线程环境也不必定须要同步。

锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。

互斥即一次只容许一个线程持有某个特定的锁,所以可以使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程可以使用该共享数据。

可见性要更加复杂一些,它必须确保释放锁以前对共享数据作出的更改对于随后得到该锁的另外一个线程是可见的 —— 若是没有同步机制提供的这种可见性保证,线程看到的共享变量多是修改前的值或不一致的值,这将引起许多严重问题

小结:为了防止多个线程并发对同一数据的修改,因此须要同步,不然会形成数据不一致(就是所谓的:线程安全。如java集合框架中Hashtable和Vector是线程安全的。咱们的大部分程序都不是线程安全的,由于没有进行同步,并且咱们没有必要,由于大部分状况根本没有多线程环境)。



二、 什么叫原子的(原子操做)?

     Java原子操做是指:不会被打断地的操做。(就是作到互斥 和可见性?!)

那难道原子操做就能够真的达到线程安全同步效果了吗?实际上有一些原子操做不必定是线程安全的。

那么,原子操做在什么状况下不是线程安全的呢?也许是这个缘由致使的:java线程容许线程在本身的内存区保存变量的副本。容许线程使用本地的私有拷贝进行工做而非每次都使用主存的值是为了提升性能(本人愚见:虽然原子操做是线程安全的,可各线程在获得变量(读操做)后,就是各自玩弄本身的副本了,更新操做(写操做)因未写入主存中,致使其它线程不可见)。

那该如何解决呢?所以须要经过java同步机制。

     在java中,32位或者更少位数的赋值是原子的。在一个32位的硬件平台上,除了double和long型的其它原始类型一般都是使用32位进行表示,而double和long一般使用64位表示。另外,对象引用使用本机指针实现,一般也是32位的。对这些32位的类型的操做是原子的。

     这些原始类型一般使用32位或者64位表示,这又引入了另外一个小小的神话:原始类型的大小是由语言保证的。这是不对的。java语言保证的是原始类型的表数范围而非JVM中的存储大小。所以,int型老是有相同的表数范围。在一个JVM上可能使用32位实现,而在另外一个JVM上多是64位的。在此再次强调:在全部平台上被保证的是表数范围,32位以及更小的值的操做是原子的。

    

三、 不要搞混了:同步、异步

举个例子:普通B/S模式(同步)AJAX技术(异步)

同步:提交请求->等待服务器处理->处理完返回 这个期间客户端浏览器不能干任何事

异步:请求经过事件触发->服务器处理(这是浏览器仍然能够做其余事情)->处理完毕

可见,彼“同步”非此“同步”——咱们说的java中的那个共享数据同步(synchronized)

一个同步的对象是指行为(动做),一个是同步的对象是指物质(共享数据)。



四、 Java同步机制有4种实现方式:(部分引用网上资源)

①    ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile

目的:都是为了解决多线程中的对同一变量的访问冲突
ThreadLocal
    ThreadLocal 保证不一样线程拥有不一样实例,相同线程必定拥有相同的实例,即为每个使用该变量的线程提供一个该变量值的副本,每个线程均可以独立改变本身的副本,而不是与其它线程的副本冲突。

优点:提供了线程安全的共享对象

与其它同步机制的区别:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通讯;而 ThreadLocal 是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源,这样固然不须要多个线程进行同步了。

volatile
     volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。并且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
    优点:这样在任什么时候刻,两个不一样的线程老是看到某个成员变量的同一个值。
    原因:Java 语言规范中指出,为了得到最佳速度,容许线程保存共享成员变量的私有拷贝,并且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必需要注意到要让线程及时的获得共享成员变量的变化。而 volatile 关键字就是提示 VM :对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
     使用技巧:在两个或者更多的线程访问的成员变量上使用 volatile 。当要访问的变量已在 synchronized 代码块中,或者为常量时,没必要使用。
        线程为了提升效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动做时才进行A和B的同步,所以存在A和B不一致的状况。volatile就是用来避免这种状况的。 volatile告诉jvm,它所修饰的变量不保留拷贝,直接访问主内存中的(读操做多时使用较好;线程间须要通讯,本条作不到)

   Volatile 变量具备 synchronized 的可见性特性,可是不具有原子特性。这就是说线程可以自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,可是只能应用于很是有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。

            您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时知足下面两个条件:

对变量的写操做不依赖于当前值;该变量没有包含在具备其余变量的不变式中。



sleep() vs wait()
sleep是线程类(Thread)的方法,致使此线程暂停执行指定时间,把执行机会给其余线程,可是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法致使本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备得到对象锁进入运行状态。

(若是变量被声明为volatile,在每次访问时都会和主存一致;若是变量在同步方法或者同步块中被访问,当在方法或者块的入口处得到锁以及方法或者块退出时释放锁时变量被同步。)浏览器

相关文章
相关标签/搜索