java并发编程这个领域中synchronized关键字一直都是元老级的角色,在java早期版本中,synchronized属于重量级锁,效率低下,由于监视器锁(monitor)是依赖于底层的操做系统的Mutex Lock来实现的,java的线程是映射到操做系统的原生线程之上的。若是要挂起或者唤醒一个线程,都须要操做系统帮忙完成,而操做系统实现线程之间的切换时须要从用户态转换到内核态,这个状态之间的转换须要相对比较长的时间,时间成本相对较高。在JDK1.6以后java官方对从JVM层面对synchronized 较大优化,因此如今的synchronized锁效率也优化得很不错。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减小锁操做的开销。java
经过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的,切换到类的对应目录执行javac SynchronizedTest.java命令生成编译后的.class文件,而后执行javap -v SynchronizedTest.class。编程
1)synchronized同步代码块并发
public class SynchronizedTest { public static void main(String[] args) { new SynchronizedTest().method(); } public void method() { synchronized (this) { System.out.println("synchronized 代码块"); } } }
从上面咱们能够看出:synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。 当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每一个Java对象的对象头中,synchronized 锁即是经过这种方式获取锁的,也是为何Java中任意对象能够做为锁的缘由) 的持有权.当计数器为0则能够成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,代表锁被释放。若是获取对象锁失败,那当前线程就要阻塞等待,直到锁被另一个线程释放为止。异步
synchronized的语义底层是经过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为何只有在同步的块或者方法中才能调用wait/notify等方法,不然会抛出java.lang.IllegalMonitorStateException的异常的缘由。ide
2)synchronized修饰方法优化
public class SynchronizedTest { public static void main(String[] args) { new SynchronizedTest().method(); } public synchronized void method() { System.out.println("Hello World!"); } }
synchronized 修饰的方法的同步并无 monitorenter 指令和 monitorexit 指令完成(理论上其实也能够经过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,若是设置了,执行线程将先获取monitor,获取成功以后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其余任何线程都没法再得到同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需经过字节码来完成。this
小结:对于同步块的实现使用了monitorenter和monitorexit指令,而同步方法则是依靠方法修饰符上的ACC_SYNCHRONIZED来完成的。不管采用哪一种方法,其本质是对一个对象的监视器(monitor)进行获取,而这个获取过程是排他的,也就是同一个时刻只能有一个线程获取到由synchronized所保护对象的监视器。spa
synchronized实现同步的基础是:Java中每一个对象均可以做为锁,具体表现为如下三种形式:
1.对于普通同步方法,锁是当前实例对象;
2.对于静态同步方法,锁是当前类的class对象;
3.对于同步方法块,锁是synchronized括号里配置的对象操作系统
1)多个线程访问的是多个对象.net
public class HasSelfPrivateNum { private int num = 0; public static void main(String[] args) { HasSelfPrivateNum numRef1 = new HasSelfPrivateNum(); HasSelfPrivateNum numRef2 = new HasSelfPrivateNum(); ThreadTestA thread1 = new ThreadTestA(numRef1); thread1.start(); ThreadTestB thread2 = new ThreadTestB(numRef2); thread2.start(); } synchronized public void add(String username) { try { if (username.equals("a")) { num = 100; System.out.println("a set over!"); Thread.sleep(2000); } else { num = 200; System.out.println("b set over!"); } System.out.println(username + " num=" + num); } catch (InterruptedException e) { e.printStackTrace(); } } static public class ThreadTestA extends Thread { private HasSelfPrivateNum numRef; public ThreadTestA(HasSelfPrivateNum numRef) { this.numRef = numRef; } @Override public void run() { numRef.add("a"); } } static public class ThreadTestB extends Thread { private HasSelfPrivateNum numRef; public ThreadTestB(HasSelfPrivateNum numRef) { this.numRef = numRef; } @Override public void run() { numRef.add("b"); } } }
两个线程ThreadTestA和ThreadTestB分别访问同一个类的不一样实例的相同名称的同步方法,可是效果确实异步执行,由于synchronized取得的锁都是对象锁,而不是把一段代码或方法当作锁。因此在上面的实例中,哪一个线程先执行带synchronized关键字的方法,则哪一个线程就持有该方法所属对象的锁Lock,那么其余线程只能呈等待状态。当前建立了两个HasSelfPrivateNum类对象,因此就产生了两个锁。当ThreadTestA的引用执行到add方法run中的Thread.sleep(2000)语句时,ThreadB就会“伺机执行”。
2)多个线程访问的是同一个对象
public static void main(String[] args) { HasSelfPrivateNum numRef = new HasSelfPrivateNum(); ThreadTestA thread1 = new ThreadTestA(numRef); thread1.start(); ThreadTestB thread2 = new ThreadTestB(numRef); thread2.start(); }
多个线程访问的是同一个对象,哪一个线程先执行带synchronized关键字的方法,则哪一个线程就持有该方法,那么其余线程只能呈等待状态。若是多个线程访问的是多个对象则不必定,由于多个对象会产生多个锁。
3)脏读
发生脏读的状况实在读取实例变量时,此值已经被其余线程更改过。
public class PublicVar { public String username = "A"; public String password = "AA"; synchronized public void setValue(String username, String password) { try { this.username = username; Thread.sleep(3000); this.password = password; System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); } catch (InterruptedException e) { e.printStackTrace(); } } //该方法前加上synchronized关键字就同步了 public void getValue() { System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); } public static void main(String[] args) { try { PublicVar publicVarRef = new PublicVar(); ThreadC thread = new ThreadC(publicVarRef); thread.start(); Thread.sleep(500);//打印结果受此值大小影响 publicVarRef.getValue(); } catch (InterruptedException e) { e.printStackTrace(); } } static class ThreadC extends Thread { private PublicVar publicVar; public ThreadC(PublicVar publicVar) { this.publicVar = publicVar; } @Override public void run() { publicVar.setValue("B", "BB"); } } }
4)静态同步synchronized方法与synchronized(class)代码块
synchronized关键字加到static静态方法和synchronized(class)代码块上都是是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
public class SynchronizedTest2 { public static void printA() { synchronized (SynchronizedTest2.class) { try { System.out.println( "线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printA"); Thread.sleep(3000); System.out.println( "线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printA"); } catch (InterruptedException e) { e.printStackTrace(); } } } synchronized public static void printB() { System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printB"); System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printB"); } synchronized public void printC() { System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printC"); System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printC"); } static class ThreadA extends Thread { private SynchronizedTest2 test; public ThreadA(SynchronizedTest2 test) { this.test = test; } @Override public void run() { test.printA(); } } static class ThreadB extends Thread { private SynchronizedTest2 test; public ThreadB(SynchronizedTest2 test) { this.test = test; } @Override public void run() { test.printB(); } } static class ThreadC extends Thread { private SynchronizedTest2 test; public ThreadC(SynchronizedTest2 test) { this.test = test; } @Override public void run() { test.printC(); } } public static void main(String[] args) { SynchronizedTest2 test = new SynchronizedTest2(); ThreadA a = new ThreadA(test); a.setName("A"); a.start(); ThreadB b = new ThreadB(test); b.setName("B"); b.start(); ThreadC c = new ThreadC(test); c.setName("C"); c.start(); } }
静态同步synchronized方法与synchronized(class)代码块持有的锁同样,都是Class锁,Class锁对对象的全部实例起做用。synchronized关键字加到非static静态方法上持有的是对象锁。线程A,B和线程C持有的锁不同,因此A和B运行同步,可是和C运行不一样步。