一、前述
我们一直都说,线程锁住的都是对象,那本文就来证明下究竟是对象还是其他的
二、synchronized 方法与锁对象
public class SynTest02 {
public void testA(){
try {
System.out.println("testA begin threadname = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testA end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread{
private SynTest02 synTest02;
public ThreadA(SynTest02 synTest02){
super();
this.synTest02 = synTest02;
}
@Override
public void run(){
super.run();
synTest02.testA();
}
}
public class ThreadB extends Thread{
private SynTest02 synTest02;
public ThreadB(SynTest02 synTest02){
super();
this.synTest02 = synTest02;
}
@Override
public void run(){
super.run();
synTest02.testA();
}
}
public class Run {
public static void main(String[] args) {
SynTest02 synTest02 = new SynTest02();
ThreadA a = new ThreadA(synTest02);
a.setName("a");
ThreadB b = new ThreadB(synTest02);
b.setName("b");
a.start();
b.start();
}
}
运行结果:
testA begin threadname = a
testA begin threadname = b
testA end
testA end
从运行结果可以看到,异步打印,没有同步的过程,a 线程还没跑完,没有打印 end,b 线程就开始进入了方法,也就是说两个线程在抢夺对象执行方法
将方法改为 synchronized 修饰,代码如下:
public class SynTest02 {
public synchronized void testA(){
try {
System.out.println("testA begin threadname = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testA end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
testA begin threadname = a
testA end
testA begin threadname = b
testA end
1、运行结果可以看到,a 线程进入方法以后,获取了对象锁,执行第一个输出,然后睡眠 5 s ,继续执行 end 的输出,紧接着 b 线程进入方法,执行代码,可以得到:是同步在进行的,两个线程排队运行。
2、学习和应用 synchronized 的时候,我们要记住,调用关键字 synchronized 修饰的时候,要记住,只有读写访问共享的资源的时候,才需要同步,如果不是共享的资源,就没必要同步竞争
3、看到这里,可能又会有疑问,该对象所属的类里面的其他方法,被访问调用的时候哦,是什么效果呢?怎么查看锁对象的效果呢,这里来看看另外一段代码:
public class SynTest02 {
public synchronized void testA(){
try {
System.out.println("testA begin threadname = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testA end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void testB(){
try {
System.out.println("testB begin threadname = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testB end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
在类中加上一个非同步的方法 testB ,然后 ThreadB 里面调用 testB 方法:
public class ThreadB extends Thread{
private SynTest02 synTest02;
public ThreadB(SynTest02 synTest02){
super();
this.synTest02 = synTest02;
}
@Override
public void run(){
super.run();
synTest02.testB();
}
}
运行结果:
testA begin threadname = abegintime = 1561198111126
testB begin threadname = bbegintime = 1561198111126
testA end , endtime = 1561198116127
testB end , endtime = 1561198116127
1、从运行结果可以看出,虽然线程 A 先拥有了该对象的锁,但是线程 B 还是可以异步的调用该对象所属里面的非同步方法,也就是没有加 synchronized 关键字修饰的方法
2、接下来将 testB 也改为 synchronized 关键字修饰
public class SynTest02 {
public synchronized void testA(){
try {
System.out.println("testA begin threadname = " + Thread.currentThread().getName()+"begintime = "+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("testA end , endtime = " + System.currentTimeMillis());
}catch (InterruptedException e){
e.printStackTrace();
}
}
public synchronized void testB(){
try {
System.out.println("testB begin threadname = " + Thread.currentThread().getName()+"begintime = "+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("testB end , endtime = "+System.currentTimeMillis());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
testA begin threadname = abegintime = 1561198343034
testA end , endtime = 1561198348036
testB begin threadname = bbegintime = 1561198348036
testB end , endtime = 1561198353036
运行结果可以i看出来,线程 A 先获取到对象的锁以后,线程 B 被阻塞,处于等待状态,因为两个线程对同一个对象访问的时候,访问同步方法,会将对象锁住,不允许其他的线程访问,也就是同步的一个过程
总结:
当线程 A、B 同时通过一个对象访问一个方法的时候,如果这个方法是被关键字 synchronized 修饰的,那么这个对象就会被锁住,只允许当前线程操作访问,如果访问不是被它修饰的方法,那么就不会被锁住,这样其他线程也可以访问它的非同步方法,异步进行