Java并发 synchronized
2018-02-22 00:00
197 查看
在多线程并发访问资源(这类资源称为临街资源)的时候,由于割裂来了原子操作,所以会导致数据不一致的情况。为了避免这种情况,需要使用同步机制,同步机制能够保证多线程并发访问数据的时候不会出现数据不一致的情况。
一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁的线程释放锁。在Java中,每个类都有一个内置锁,之所以如此,是因为Java并发专家认为这样可以避免显式创建锁。
通过下面的代码可以直到synchronized到底是如何实现的:
使用java -v Synchronized.class命令可以看到如下的结果:
通过编译的class文件可以看到synchronized代码块使用了monitorenter和monitorexit两个指令分别获取锁标记和释放锁标记,而synchronized方法使用了ACC_SYNCHRONIZED来完成锁的获取与释放的。也就是锁的获取与释放synchronized关键字自动帮我们完成了。
对于使用synchronized关键字实现的同步机制由如下几点补充说明:
如果同一个方法有多个线程访问,那么每个线程都有自己的线程拷贝(拷贝存储在工作内存中)
类的实例都有自己的对象锁,如果一个线程成功获取到该实例的对象锁那么当其他线程需要获取该实例的对象锁时,便需要阻塞等待,直到该实例的对象锁被成功释放。对象锁可以作用在同步方法或者同步代码块中
如果不同的线程访问的是不同实例的对象锁,那么不会互相阻塞,因为不同实例的对象锁是不同的
获得实例的对象锁的线程会让其他想要获取相同对象锁的线程阻塞在synchronized代码外,比如由两个synchronized方法a()、b(),那么如果线程A成功进入了同步方法a(),那么该线程便获取了该实例(比如实例obj)的对象锁,而如果其他线程想要执行另一个同步方法b(),就会阻塞在外面,因为a()和b()持有的都是对象obj的对象锁
持有一个实例的对象锁不会阻止该线程被置换出来,也不会阻塞其他线程执行非synchronized方法,因为非synchronized方法执行的时候不需要获取实例的对象锁
使用同步代码块的时候,括号的对象可以为任意Object实例,当为this时,指的是当前对象的对象锁
类锁主要用于控制对static成员变量的并发访问
synchronized块(可以是同步方法或者同步代码块)是可重入的,每次重入会把锁的计数器加1,每次退出将计数器减1,当计数器的值为0的时候,锁便被释放了
Java SE 1.6 为了减少获得锁和释放锁的性能消耗引入了偏向锁和轻量级锁,所以使用synchronized也没有那么重量级了
关于synchronized的内存可见性做如下说明:
线程B在执行由锁保护的同步代码块时,可以看到线程A对同一个锁保护的同步代码块的操作结果。举个例子,A获取对象M的对象级别锁,并进入代码块,随后释放对象M的对象级别锁,在释放后由线程B获取到了对象M的对象级别锁,那么B就能看到线程A操作的结果,反之,如果线程B在释放锁后,线程A才获取到锁,那么B就不一定能够看到A操作的结果了。
对内存可见性的保证是基于happen-before原则的,任何对锁的获取happen-before于对锁的释放
从内存可见性的角度volatile与锁由相同的语义,写volatile变量相当于退出同步代码块,读volatile变量相当于进入同步代码块
锁机制可以保证原子性和可见性,而volatile只能保证可见性。在需要同步的时候应该优先使用synchronized
当且仅当对变量的写入不依赖当前值以及该变量包含在具有其他变量的不变式(比如i++)的时候才可以使用volatile
一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁的线程释放锁。在Java中,每个类都有一个内置锁,之所以如此,是因为Java并发专家认为这样可以避免显式创建锁。
通过下面的代码可以直到synchronized到底是如何实现的:
package com.rhwayfun.concurrency; /** * Created by rhwayfun on 16-4-3. */ public class Synchronied { public static void main(String[] args){ synchronized (Synchronied.class){ } a(); } public static synchronized void a() { } }
使用java -v Synchronized.class命令可以看到如下的结果:
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc #2 // class com/rhwayfun/concurrency/Synchronied 2: dup 3: astore_1 4: monitorenter 5: aload_1 6: monitorexit 7: goto 15 10: astore_2 11: aload_1 12: monitorexit 13: aload_2 14: athrow 15: invokestatic #3 // Method a:()V 18: return Exception table: from to target type 5 7 10 any 10 13 10 any LineNumberTable: line 8: 0 line 10: 5 line 11: 15 line 12: 18 LocalVariableTable: Start Length Slot Name Signature 0 19 0 args [Ljava/lang/String; public static synchronized void a(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 15: 0 } SourceFile: "Synchronied.java"
通过编译的class文件可以看到synchronized代码块使用了monitorenter和monitorexit两个指令分别获取锁标记和释放锁标记,而synchronized方法使用了ACC_SYNCHRONIZED来完成锁的获取与释放的。也就是锁的获取与释放synchronized关键字自动帮我们完成了。
对于使用synchronized关键字实现的同步机制由如下几点补充说明:
如果同一个方法有多个线程访问,那么每个线程都有自己的线程拷贝(拷贝存储在工作内存中)
类的实例都有自己的对象锁,如果一个线程成功获取到该实例的对象锁那么当其他线程需要获取该实例的对象锁时,便需要阻塞等待,直到该实例的对象锁被成功释放。对象锁可以作用在同步方法或者同步代码块中
如果不同的线程访问的是不同实例的对象锁,那么不会互相阻塞,因为不同实例的对象锁是不同的
获得实例的对象锁的线程会让其他想要获取相同对象锁的线程阻塞在synchronized代码外,比如由两个synchronized方法a()、b(),那么如果线程A成功进入了同步方法a(),那么该线程便获取了该实例(比如实例obj)的对象锁,而如果其他线程想要执行另一个同步方法b(),就会阻塞在外面,因为a()和b()持有的都是对象obj的对象锁
持有一个实例的对象锁不会阻止该线程被置换出来,也不会阻塞其他线程执行非synchronized方法,因为非synchronized方法执行的时候不需要获取实例的对象锁
使用同步代码块的时候,括号的对象可以为任意Object实例,当为this时,指的是当前对象的对象锁
类锁主要用于控制对static成员变量的并发访问
synchronized块(可以是同步方法或者同步代码块)是可重入的,每次重入会把锁的计数器加1,每次退出将计数器减1,当计数器的值为0的时候,锁便被释放了
Java SE 1.6 为了减少获得锁和释放锁的性能消耗引入了偏向锁和轻量级锁,所以使用synchronized也没有那么重量级了
关于synchronized的内存可见性做如下说明:
线程B在执行由锁保护的同步代码块时,可以看到线程A对同一个锁保护的同步代码块的操作结果。举个例子,A获取对象M的对象级别锁,并进入代码块,随后释放对象M的对象级别锁,在释放后由线程B获取到了对象M的对象级别锁,那么B就能看到线程A操作的结果,反之,如果线程B在释放锁后,线程A才获取到锁,那么B就不一定能够看到A操作的结果了。
对内存可见性的保证是基于happen-before原则的,任何对锁的获取happen-before于对锁的释放
从内存可见性的角度volatile与锁由相同的语义,写volatile变量相当于退出同步代码块,读volatile变量相当于进入同步代码块
锁机制可以保证原子性和可见性,而volatile只能保证可见性。在需要同步的时候应该优先使用synchronized
当且仅当对变量的写入不依赖当前值以及该变量包含在具有其他变量的不变式(比如i++)的时候才可以使用volatile
相关文章推荐
- 【Java并发编程实战】-----synchronized
- Java并发编程:synchronized
- Java并发编程:synchronized
- Java 并发开发:内置锁 Synchronized
- 【Java集合类型的并发】Collections.synchronizedList
- java多线程基础值对象和变量的并发访问之synchronized(二)
- 深入理解Java并发之synchronized实现原理
- 【Java并发编程】之七:使用synchronized获取互斥锁的几点说明 (r)
- 【java并发】传统线程互斥技术—synchronized
- Java并发编程:synchronized
- Java并发与synchronized关键字
- Java并发编程之同步关键字synchronized
- java并发之synchronized
- 【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
- Java并发--互斥同步--synchronized和ReentrantLock比较
- java并发之Lock与synchronized的区别
- 深入理解Java并发之synchronized实现原理
- java并发编程学习之一段简单代码证明synchronized锁的是对象
- 【死磕Java并发】—–深入分析synchronized的实现原理
- java多线程并发(一)Semaphore,volatile,synchronized ,Lock, CyclicBarrier和CountDownLatch