zoukankan      html  css  js  c++  java
  • synchronized详解

    synchronized关键字主要用于对普通方法,静态方法和代码块

    synchronized用于代码块

    package Synchronized;
    
    public class Test02 {
    	public void method1(){
    		System.out.println("method 1 start");
    		try{
    			synchronized(this){
    				System.out.println("method 1 execute");
    				Thread.sleep(3000);
    			}
    			
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 1 end");
    	}
    	public void method2(){
    		System.out.println("method 2 start");
    		try{
    			synchronized(this){
    				System.out.println("method 2 execute");
    				Thread.sleep(1000);
    			}
    			
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 2 end");
    	}
    	public static void main(String[] args){
    		Test02 test = new Test02();
    		new Thread(new Runnable(){
    			public void run(){
    				test.method1();
    			}
    		}).start();;
    		new Thread(new Runnable(){
    			public void run(){
    				test.method2();
    			}
    		}).start();;
    	}
    }
    输出:
    method 1 start
    method 1 execute
    method 2 start
    method 2 execute
    method 1 end
    method 2 end
    

    虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。

    对以下代码反编译

    public class T {
    	//同步代码块,使用monitor监视器
    	public void method3() {
    		synchronized(this){
    			System.out.println("Hello World!");
    		}
    	}
    }
    

    得到

    Compiled from "T.java"
    public class Synchronized.T extends java.lang.Object{
    public Synchronized.T();
      Code:
       0:	aload_0
       1:	invokespecial	#8; //Method java/lang/Object."<init>":()V
       4:	return
    
    public void method3();
      Code:
       0:	aload_0
       1:	dup
       2:	astore_1
       3:	monitorenter
       4:	getstatic	#15; //Field java/lang/System.out:Ljava/io/PrintStream;
       7:	ldc	#21; //String Hello World!
       9:	invokevirtual	#23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       12:	aload_1
       13:	monitorexit
       14:	goto	20
       17:	aload_1
       18:	monitorexit
       19:	athrow
       20:	return
      Exception table:
       from   to  target type
         4    14    17   any
        17    19    17   any
    
    }
     

    每个对象有一个监视器锁(monitor)。synchronized同步代码块时,线程便会执行monitorenter指令时尝试获取monitor的所有权,过程如下:

    1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

    2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

    3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。   

    4.执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 

    synchronized用于普通方法

    //对普通方法同步
    public class Test01 {
    	public synchronized void method1(){
    		System.out.println("method 1 start");
    		try{
    			System.out.println("method 1 execute");
    			Thread.sleep(100);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 1 end");
    	}
    	public synchronized void method2(){
    		System.out.println("method 2 start");
    		try{
    			System.out.println("method 2 execute");
    			Thread.sleep(300);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 2 end");
    	}
    	public static void main(String[] args){
    		Test01 test = new Test01();
    		new Thread(new Runnable(){
    			public void run(){
    				test.method1();
    			}
    		}).start();;
    		new Thread(new Runnable(){
    			public void run(){
    				test.method2();
    			}
    		}).start();;
    	}
    }
    输出:
    method 1 start
    method 1 execute
    method 1 end
    method 2 start
    method 2 execute
    method 2 end
    

     可见,线程2对test对象的method2方法的执行需要等待线程1对test对象的method1方法的执行结束后才能执行。

    方法的同步并没有通过指令monitorenter和monitorexit来完成,不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

    synchronized用于静态方法

    package Synchronized;
    
    public class Test03 {
    	public synchronized static void method1(){
    		System.out.println("method 1 start");
    		try{
    			System.out.println("method 1 execute");
    			Thread.sleep(100);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 1 end");
    	}
    	public synchronized static void method2(){
    		System.out.println("method 2 start");
    		try{
    			System.out.println("method 2 execute");
    			Thread.sleep(300);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    		System.out.println("method 2 end");
    	}
    	public static  void main(String[] args){
    		Test03 test1 = new Test03();
    		Test03 test2 = new Test03();
    		new Thread(new Runnable(){
    			public void run(){
    				test1.method1();
    			}
    		}).start();;
    		new Thread(new Runnable(){
    			public void run(){
    				test2.method2();
    			}
    		}).start();;
    	}
    }
    输出:
    method 1 start
    method 1 execute
    method 1 end
    method 2 start
    method 2 execute
    method 2 end
    

    执行结果如下,对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。

    总结

    synchronized是通过软件(JVM)实现的,简单易用,即使在JDK5之后有了Lock,仍然被广泛地使用。

    synchronized实际上是非公平的,新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,不过这种抢占的方式可以预防饥饿。

    synchronized只有锁只与一个条件(是否获取锁)相关联,不灵活,后来Condition与Lock的结合解决了这个问题。

    多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。高并发的情况下会导致性能下降。ReentrantLock的lockInterruptibly()方法可以优先考虑响应中断。 一个线程等待时间过长,它可以中断自己,然后ReentrantLock响应这个中断,不再让这个线程继续等待。有了这个机制,使用ReentrantLock时就不会像synchronized那样产生死锁了。

  • 相关阅读:
    笨蛋的厄运
    模仿写了一个摸鱼应用解决原作者的问题
    retain和copy还有assign的区别
    分析与理解通知消息WM_NOTIFY
    备忘录
    数据结构C++模板实现之单向链表
    服务器(VPS合租)
    复制构造,赋值操作符,const重要性
    Windows 注册表操作简介
    窗口类封装之窗口对象消息处理的映射方法(1)
  • 原文地址:https://www.cnblogs.com/wuchaodzxx/p/6867546.html
Copyright © 2011-2022 走看看