当多个线程调用同一个资源类中的同步方法和非同步方法,它们的执行顺序是什么?看如下代码
public class MyObject { synchronized public void methodA() { try { System.out.println("begin methodA threadName=" + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end endTime=" + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } //非同步方法 public void methodB() { try { System.out.println("begin methodB threadName=" + Thread.currentThread().getName() + " begin time=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class ThreadA extends Thread { private MyObject object; public ThreadA(MyObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } } public class ThreadB extends Thread { private MyObject object; public ThreadB(MyObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.methodB(); } }
public class Run { public static void main(String[] args) { MyObject object = new MyObject(); ThreadA a = new ThreadA(object); a.setName("A"); ThreadB b = new ThreadB(object); b.setName("B"); a.start(); b.start(); } }
有上面可以知道 线程A先持有对象的锁,但是线程B完全可以异步调用非synchronized的方法。
如果把上面的methodB()也加上synchronized,则A线程在调用methodA()同步方法的时候获得了对象的锁。B线程在调用对象的其他同步方法则需要等待A线程执行完,再执行B线程。
下面演示脏读的情况
public class ThreadA extends Thread { private PublicVar publicVar; public ThreadA(PublicVar publicVar) { super(); this.publicVar = publicVar; } @Override public void run() { super.run(); publicVar.setValue("B", "BB"); } } public class Test { public static void main(String[] args) { try { PublicVar publicVarRef = new PublicVar(); ThreadA thread = new ThreadA(publicVarRef); thread.start(); Thread.sleep(200);// 打印结果受此值大小影响 //这里取值 publicVarRef.getValue(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
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(5000); this.password = password; System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); } catch (InterruptedException e) { e.printStackTrace(); } } public void getValue() { System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); }
如果getValue()方法加上synchronized 不会出现这种情况。
synchronized锁重入介绍
当一个线程得到一个对象的锁时,如果再次请求此对象的锁时 是 可以再次湖区该对象的锁的,这也证明在一个synchronized方法/块的内部调用本类中的其他synchronized的方法/块时,是永远可以得到锁的。如下代码验证。
public class Service { synchronized public void service1() { System.out.println("service1"); service2(); } synchronized public void service2() { System.out.println("service2"); service3(); } synchronized public void service3() { System.out.println("service3"); } }
public class MyThread extends Thread { @Override public void run() { //线程调用资源类中的同步方法 Service service = new Service(); service.service1(); } }
public class Run { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }
再来说下“可重入锁”的概念:自己可以再次虎丘自己的内部锁。比如一个线程获取了某个对象的锁,此时这个对象的锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可以获取,就会造成死锁。
可重入锁支持父子类继承的环境中。如下代码
public class Main { public int i = 10; synchronized public void operateIMainMethod() { try { i--; System.out.println("main print i=" + i); Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Sub extends Main { synchronized public void operateISubMethod() { try { while (i > 0) { i--; System.out.println("sub print i=" + i); Thread.sleep(100); this.operateIMainMethod(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class MyThread extends Thread { @Override public void run() { //子类操作资源类 Sub sub = new Sub(); sub.operateISubMethod(); } }
public class Run { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }
当存在父子类关系时,子类完全可以通过可冲入锁,调用父类的同步方法。
这里还有一个重要的知识点就是:如果一个线程出现异常就会释放锁。
同步不具有继承性
public class Main { synchronized public void serviceMethod() { try { System.out.println("int main 下一步sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int main 下一步sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Sub extends Main { @Override public void serviceMethod() { try { System.out.println("int sub 下一步sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int sub 下一步sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Test { public static void main(String[] args) { Sub subRef = new Sub(); MyThreadA a = new MyThreadA(subRef); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(subRef); b.setName("B"); b.start(); } }
程序异步执行
子类重写的方法加上synchronized后 程序同步执行