2.1.5 synchronized方法将对象作为锁
为了证明前面讲的将对象作为锁,创建实验项目synchronizedMethodLockObject,类MyObject.java文件代码如下:
package extobject; public class MyObject { public void methodA() { try { System.out.println("begin methodA threadName=" + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end"); } catch (InterruptedException e) { e.printStackTrace(); } } }
自定义线程类ThreadA.java代码如下:
package extthread; import extobject.MyObject; public class ThreadA extends Thread { private MyObject object; public ThreadA(MyObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } }
自定义线程类ThreadB.java代码如下:
package extthread; import extobject.MyObject; public class ThreadB extends Thread { private MyObject object; public ThreadB(MyObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } }
运行类Run.java代码如下:
package test.run; import extobject.MyObject; import extthread.ThreadA; import extthread.ThreadB; 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(); } }
程序运行结果如图2-6所示。
图2-6 两个线程可一同进入methodA方法
两个线程可一同进入methodA方法,因为该方法并没有同步化。
更改MyObject.java代码如下:
package extobject; public class MyObject { synchronized public void methodA() { try { System.out.println("begin methodA threadName=" + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end"); } catch (InterruptedException e) { e.printStackTrace(); } } }
如上面代码所示,在methodA方法前加入关键字synchronized进行同步处理。程序再次运行的结果如图2-7所示。
图2-7 排队进入方法
通过上面的示例得到结论,调用用关键字synchronized声明的方法一定是排队运行。另外,需要牢牢记住“共享”这两个字,只有共享资源的写访问才需要同步化,如果不是共享资源,那么就没有同步的必要。
那其他方法在被调用时会是什么效果呢?如何查看将对象作为锁的效果呢?继续新建实验项目synchronizedMethodLockObject2,类文件MyObject.java代码如下:
package extobject; 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(); } } }
两个自定义线程类分别调用不同的方法,代码如图2-8所示。
图2-8 调用不同方法的线程类
文件Run.java代码如下:
package test.run; import extobject.MyObject; import extthread.ThreadA; import extthread.ThreadB; 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(); } }
程序运行结果如图2-9所示。
图2-9 线程B异步调用非同步方法
通过上面的实验可以得知,虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronized类型的方法。
继续实验,在MyObject.java文件中的methodB()方法前加入synchronized关键字,代码如下:
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"); } catch (InterruptedException e) { e.printStackTrace(); } }
图2-10 同步运行
本示例是两个线程访问同一个对象的两个同步方法,运行结果如图2-10所示。
结论如下:
1)A线程先持有object对象的锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
2)A线程先持有object对象的锁,B线程如果在这时调用object对象中的synchronized类型的方法,则需等待,也就是同步。
3)在方法声明处添加synchronized并不是锁方法,而是锁当前类的对象。
4)在Java中只有将对象作为锁,并没有锁方法这种说法。
5)在Java语言中,锁就是对象,对象可以映射成锁,哪个线程拿到这把锁,哪个线程就可以执行这个对象中的synchronized同步方法。
6)如果在X对象中使用了synchronized关键字声明非静态方法,则X对象就被当成锁。