多线程(二)Synchronized
synchronized是Java中的关键字,是一种同步锁,按类型可以分为同步方法和同步代码块。
非同步实例方法&同步实例方法:
非同步实例方法:
public class TestSync { public static void main(String[] args) throws InterruptedException { Cls c = new Cls(); new Thread() { @Override public void run() { try { c.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread() { @Override public void run() { try { c.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } class Cls { public void method() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod end.."); } }
执行结果(场景:同步方法属于同一个实例对象):
Thread-0 menthod begin.. Thread-1 menthod begin.. Thread-1 menthod end.. Thread-0 menthod end..
同步实例方法:
public class TestSync { public static void main(String[] args) throws InterruptedException { Cls c = new Cls(); new Thread() { @Override public void run() { try { c.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread() { @Override public void run() { try { c.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } class Cls { public synchronized void method() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod1 begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod1 end.."); } }
执行结果:
Thread-0 menthod1 begin.. Thread-0 menthod1 end.. Thread-1 menthod1 begin.. Thread-1 menthod1 end..
同步实例方法&同步静态方法:
同步实例方法(场景:同步方法属于不同的实例对象):
public class TestSync { public static void main(String[] args) throws InterruptedException { Cls c = new Cls(); new Thread() { @Override public void run() { try { c.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); Cls c1 = new Cls(); new Thread() { @Override public void run() { try { c1.method(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } class Cls { public static synchronized void staticMethod() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " staticMenthod begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " staticMenthod end.."); } public synchronized void method() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod end.."); } }
Thread-1 menthod begin.. Thread-0 menthod begin.. Thread-0 menthod end.. Thread-1 menthod end..
同步静态方法:
public class TestSync { public static void main(String[] args) throws InterruptedException { new Thread() { @Override public void run() { try { Cls.staticMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread() { @Override public void run() { try { Cls.staticMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } class Cls { public static synchronized void staticMethod() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " staticMenthod begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " staticMenthod end.."); } public synchronized void method() throws InterruptedException { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod begin.."); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " menthod end.."); } }
执行结果:
Thread-0 staticMenthod begin.. Thread-0 staticMenthod end.. Thread-1 staticMenthod begin.. Thread-1 staticMenthod end..
结论:
- 被锁对象是类的实例对象时,synchronized只在同一实例中作用
- 被锁对象是类对象时,即使new多个实例对象,但他们仍然是属于同一个类,依然会被锁住,即线程之间保证同步关系。
锁机制
public class TestSync { public void lockClass() { synchronized(TestSync.class) { System.out.println("lockClass .."); } } public synchronized void lockObject() { System.out.println("lockObject .."); } }
使用【javap -v TestSync.class】来反编译以上代码,结果如下:
public void lockClass(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: ldc #1 // class com/ryj/thread/TestSync 2: dup 3: astore_1 4: monitorenter 5: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #21 // String lockClass .. 10: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: aload_1 14: monitorexit 15: goto 21 18: aload_1 19: monitorexit 20: athrow 21: return public synchronized void lockObject(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #35 // String lockObject .. 5: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return
对于同步方法,JVM采用ACC_SYNCHRONIZED
标记符来实现同步。 对于同步代码块。JVM采用monitorenter
、monitorexit
两个指令来实现同步。
方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED
标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED
,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。
执行monitorenter
指令理解为加锁,执行monitorexit
理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter
)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit
指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。
Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统的帮忙,这就要从用户态转换到核心态,因此状态转换需要花费很多的处理器时间,对于代码简单的同步块(如被synchronized修饰的get 或set方法)状态转换消耗的时间有可能比用户代码执行的时间还要长,所以说synchronized是java语言中一个重量级的操纵。