本篇主要说明synchronized方法的一些属性
总结:
1,局部变量是线程安全的
2,同步是针对同一个对象,如果是同一类的不同实例,是异步的,不需要同步
同一对象的不同synchronized方法是同步调用的,而同一类的非syn方法可以异步调用,
3,锁具有可重入性:获取对象锁后可以直接获取同类及其父类的其他syn方法的锁
4,异常发生会释放锁
5,syn不会被继承:子类重写父类的syn方法,不会继承其syn属性
1,成员变量是非线程安全的,而局部变量永远是线程安全的
示例:
package chapter2.t1; public class ClassA { public void modifyI(String username){ try{ int i=0; if("a".equals(username)){ i=100; System.out.println("a set over"); Thread.sleep(1000); }else{ i=200; System.out.println("b set over"); } System.out.println(username+": i="+i); }catch(InterruptedException e){ e.printStackTrace(); } } }
package chapter2.t1; public class ThreadA extends Thread { private ClassA a; public ThreadA(ClassA a){ super(); this.a=a; } public void run(){ super.run(); a.modifyI("a"); } }
package chapter2.t1; public class ThreadB extends Thread { private ClassA a; public ThreadB(ClassA a){ super(); this.a=a; } public void run(){ super.run(); a.modifyI("b"); } }
package chapter2.t1; /* * 演示局部变量(方法内变量的线程安全性,只有成员变量才会有线程安全问题. */ public class Run { public static void main(String[] args) { ClassA a=new ClassA(); ThreadA ta=new ThreadA(a); ThreadB tb=new ThreadB(a); ta.start(); tb.start(); // 输出为: // a set over // b set over // b: i=200 // a: i=100 // new Run().test(); } public void test(){ //方法内部的变量只能用final修饰,因为局部变量没有权限一说,方法外是不能访问的 // private int a=0; // System.out.println(a); } }
如果将ClassA中的i变量变为成员变量,那么输出为:i的值呗b方法覆盖了
b set over a set over b: i=200 a: i=200
如果i是成员变量,实现线程安全的方法是对modifyI方法加上synchronized修饰
package chapter2.t3; /* * 和t2的非线程安全相比,这边对方法加上synchronize,也能实现成员变量的线程安全 */ public class ClassA { private int i=0; synchronized public void modifyI(String username){ try{ if("a".equals(username)){ i=100; System.out.println("a set over"); Thread.sleep(1000); }else{ i=200; System.out.println("b set over"); } System.out.println(username+": i="+i); }catch(InterruptedException e){ e.printStackTrace(); } } }
两个线程访问同一对象的同步方法是是线程安全的,但是会造成线程阻塞状态
2,同步与异步的区别
将上面例子的i修改为成员变量,并且用synchronized修饰方法,但是在run中传入两个对象如下:
package chapter2.twoObjectTwoLock; /* * 演示同步(synchronized)和异步(asynchronized)的区别 * 同一个对象,实现的是同步,多个对象实现的是异步 * synchronized加锁的对象是类实例,如果是同一个实例,会出现等待上一个线程释放锁 * 如果是多个对象,那么JVM会对不同的对象创建不同的锁,没有等待,也就是异步 */ public class Run { public static void main(String[] args) { ClassA a=new ClassA(); ClassA b=new ClassA(); ThreadA ta=new ThreadA(a); ThreadB tb=new ThreadB(b); ta.start(); tb.start(); // 输出为: // a set over // b set over // b: i=200 // a: i=100 // new Run().test(); } }
b线程没有等待a,直接输出了,并且a的值也没有被覆盖,所以异步是不同对象获得不同锁,同步是同一对象获得同步方法的同一个锁,
同一个对象的多个方法的同步关系:
示例:
package chapter2.synchroniezedMethodLockObject2; public class MyObject { // public void methodA(){ synchronized public void methodA(){ try{ System.out.println("begin methodA threadname= "+ Thread.currentThread().getName()+ " begin time="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end methodA,time="+System.currentTimeMillis()); }catch(InterruptedException e){ e.printStackTrace(); } } // public void methodB(){ synchronized public void methodB(){ try{ System.out.println("begin methodB threadname= "+ Thread.currentThread().getName()+ " begin time="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end methodB,time="+System.currentTimeMillis()); }catch (InterruptedException e) { e.printStackTrace(); } } }
package chapter2.synchroniezedMethodLockObject2; public class ThreadA extends Thread { private MyObject myobj; public ThreadA(MyObject obj){ super(); this.myobj=obj; } @Override public void run(){ super.run(); myobj.methodA(); } }
package chapter2.synchroniezedMethodLockObject2; public class ThreadB extends Thread { private MyObject myobj; public ThreadB(MyObject obj){ super(); this.myobj=obj; } @Override public void run(){ super.run(); myobj.methodB(); } }
package chapter2.synchroniezedMethodLockObject2; /* * 演示两个线程调用同一个对象的不同方法,其中一个是同步的方法, * 结论是threadB可以异步调用非同步方法methodb, * 如果methodB也是同步方法,那么thradb需要等到threada线程结束才能执行同一个对象的另一个synchronized方法 */ public class Run { public static void main(String[] args) { MyObject obj=new MyObject(); ThreadA ta=new ThreadA(obj); ta.setName("a"); ThreadB tb=new ThreadB(obj); tb.setName("b"); ta.start(); tb.start(); // 输出为: // begin methodB threadname= b begin time=1525254799564 // begin methodA threadname= a begin time=1525254799564 // end methodA,time=1525254804565 // end methodB,time=1525254804565 // 如果methodB也是同步方法,输出为: // begin methodA threadname= a begin time=1525255141141 // end methodA,time=1525255146142 // begin methodB threadname= b begin time=1525255146143 // end methodB,time=1525255151143 } }
结论:
A线程先持有object对象的lock锁,B线程可以以异步的方式调用object对象的非synchronized方法,
A线程先持有object对象的lock锁,,B线程如果调用object对象的synchronized方法,需要等待,也就是同步
3,可重入锁
关键字synchronized拥有锁重入的功能:
当一个线程得到一个对象锁之后,再次请求此对象锁是可以再次得到该对象锁的,也就是说,在一个synchronized方法/块的内部调用本类的其他synchronized方法/块总是可以得到的,另外可重入锁也支持在父子类继承的场景,也就是获得子类锁的线程可以直接获得父类的锁
可重入锁的作用是防止死锁的出现,看下面代码:
package chapter2.synLockIn_2; /* * 本方法演示线程获得子类对象的锁,可以通过可重入锁的特性获得父类的锁 */ public class Run { public static void main(String[] args) { MyThread mt=new MyThread(); mt.start(); } } package chapter2.synLockIn_2; public class MyThread extends Thread { @Override public void run(){ Sub sb=new Sub(); sb.operateISubMethod(); } } package chapter2.synLockIn_2; public class Main { public int i=11; synchronized public void operateIMainMethod(){ try{ i--; System.out.println("main print i="+i); Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } } package chapter2.synLockIn_2; public class Sub extends Main { synchronized public void operateISubMethod(){ try{ while(i>0){ i--; System.out.println("sub print i="+i); Thread.sleep(1000); this.operateIMainMethod(); } }catch(InterruptedException e){ e.printStackTrace(); } } }
输出为:sub print i=10 main print i=9 sub print i=8 main print i=7 sub print i=6 main print i=5 sub print i=4 main print i=3 sub print i=2 main print i=1 sub print i=0 main print i=-1
4,异常释放锁
线程获得对象锁后,如果执行过程中发生异常,会释放锁
示例:
package chapter2.throwExceptioinNoLock; /* * 演示在a线程获取到对象锁后,执行过程发生异常(不论是否捕获异常),会释放锁,b线程得以执行 Thread name=a run begin time=1525455640331 Thread name=a run exception time=1525455641371 NumberFormatException 被捕获 ThreadB run time=1525455641371 */ public class Run { public static void main(String[] args) throws InterruptedException { Service sv=new Service(); ThreadA ta=new ThreadA(sv); ThreadB tb=new ThreadB(sv); ta.setName("a"); tb.setName("b"); Thread.sleep(500); ta.start(); tb.start(); } } package chapter2.throwExceptioinNoLock; public class ThreadA extends Thread { private Service sv=new Service(); public ThreadA(Service sv) { this.sv=sv; } @Override public void run(){ sv.testMethod(); } } package chapter2.throwExceptioinNoLock; public class ThreadB extends Thread { private Service sv=new Service(); public ThreadB(Service sv) { this.sv=sv; } @Override public void run(){ sv.testMethod(); } } package chapter2.throwExceptioinNoLock; public class Service { synchronized public void testMethod(){ if(Thread.currentThread().getName().equals("a")){ try{ System.out.println("Thread name="+Thread.currentThread().getName()+ " run begin time="+System.currentTimeMillis() ); while(true){ if((""+Math.random()).substring(0, 8).equals("0.123456")){ System.out.println("Thread name="+Thread.currentThread().getName()+ " run exception time="+System.currentTimeMillis() ); Integer.parseInt("a"); } } }catch(NumberFormatException e){ System.out.println("NumberFormatException 被捕获"); } }else{ System.out.println("ThreadB run time="+System.currentTimeMillis()); } } }
5,同步不具有继承性
synchronized方法被子类重写后,同步属性不会被继承,子类同步由子类方法是syn决定
示例:
package synNotExtends; /* * 演示子类非syn方法重写父类syn方法,子类方法不会继承父类的syn属性, * 线程b和线程a同时调用子类的非syn方法,但是在执行父类syn方法时是同步的,b需要等待a执行完毕才能进入 * in Sub thread name=b sleep begin time=1525456850027 第一行第二行显示a,b线程同时调用子类方法 in Sub thread name=a sleep begin time=1525456850027 in Sub thread name=b sleep end time=1525456855027 in Sub thread name=a sleep end time=1525456855027 同时sleep结束调用父类syn方法 in main thread name=a sleep begin time=1525456855028 a先进入,b等待 in main thread name=a sleep end time=1525456860028 in main thread name=b sleep begin time=1525456860029 b等待a sleep后再进入 in main thread name=b sleep end time=1525456865029 */ public class Run { public static void main(String[] args) { Sub sb=new Sub(); ThreadA ta=new ThreadA(sb); ThreadB tb=new ThreadB(sb); ta.setName("a"); tb.setName("b"); ta.start(); tb.start(); } } package synNotExtends; public class Main { synchronized public void serviceMethod(){ try { System.out.println("in main thread name="+Thread.currentThread().getName() +" sleep begin time="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("in main thread name="+Thread.currentThread().getName() +" sleep end time="+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } package synNotExtends; public class Sub extends Main{ @Override public void serviceMethod(){ try { System.out.println("in Sub thread name="+Thread.currentThread().getName() +" sleep begin time="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("in Sub thread name="+Thread.currentThread().getName() +" sleep end time="+System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } } package synNotExtends; public class ThreadA extends Thread { private Sub sb; public ThreadA(Sub sb){ super(); this.sb=sb; } @Override public void run(){ sb.serviceMethod(); } } package synNotExtends; public class ThreadB extends Thread { private Sub sb; public ThreadB(Sub sb){ super(); this.sb=sb; } @Override public void run(){ sb.serviceMethod(); } }
参考:<java多线程编程核心技术>