场景一:synchronized包裹代码块

一.synchronized(对象) — 对象锁


  假设类对象instance的某段代码块被synchronized(obj){}包裹,线程访问该段代码块时便会拿到obj对象的内置锁。obj对象的内置锁释放前,其他线程仍然可以访问instance对象非同步的方法和代码块(现象一),但是:
  1. 不能进入任何也以obj为锁的代码块;(现象二)
  2. 当obj与instance是同一个对象时,也不能进入任何instance对象的同步方法(现象三)
  由此可见,第一个线程拿到obj对象的内置锁其实就相当于给instance这个对象加上了一个用于同步的独占排他锁(可重入),我们称obj对象为对象锁。

  特别说明:对于不同的两个对象,只有当请求的对象锁相同时,线程间才会产生竞争!其他情况下, 并不会互相影响。(现象四)


a.验证现象一
为类SynchronizedVerify添加如下main()方法:

public static void main(String[] args){
    final SynchronizedVerify sv = new SynchronizedVerify();
    new Thread(){//定为线程一
        public void run() { sv.blockStyle(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { sv.blockNormal(); };
    }.start();
}


sv.blockStyle() 以私有成员ObjA为对象锁
+ sv.blockNormal() 非同步方法且不含同步代码块
输出(无竞争):
blockStyle:0    |Thread-0
blockNormal:0   |Thread-1
blockStyle:1    |Thread-0
blockNormal:1   |Thread-1
blockNormal:2   |Thread-1
blockStyle:2    |Thread-0

b.验证现象二
为类SynchronizedVerify添加如下main()方法:

public static void main(String[] args){
    final SynchronizedVerify sv = new SynchronizedVerify();
    new Thread(){//定为线程一
        public void run() { sv.blockStyle(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { sv.blockContrast(); };
    }.start();
}


sv.blockStyle() 以私有成员ObjA为对象锁
+ sv.blockContrast() 以私有成员ObjA为对象锁
输出(竞争):
blockStyle:0    |Thread-0
blockStyle:1    |Thread-0
blockStyle:2    |Thread-0
blockContrast:0 |Thread-1
blockContrast:1 |Thread-1
blockContrast:2 |Thread-1


将线程二的调用改为sv.blockDiffObj() 以私有成员ObjB为对象锁
sv.blockStyle() + sv.blockDiffObj()输出(无竞争。多尝试几遍,或者加大输出量):
blockStyle:0    |Thread-0
blockStyle:1    |Thread-0
blockDiffObj:0  |Thread-1
blockStyle:2    |Thread-0
blockDiffObj:1  |Thread-1
blockDiffObj:2  |Thread-1

  组合sv.blockStyle() + sv.blockDiffObj()中,两个方法体都用synchronized包裹。但所施加的对象锁一个是objA一个是objB,这是两个对象,所以线程进入两个方法的同步代码块时并不会产生竞争。


c. 验证现象三
为类SynchronizedVerify添加如下main()方法:

public static void main(String[] args){
    final SynchronizedVerify sv = new SynchronizedVerify();
    new Thread(){//定为线程一
        public void run() { sv.blockStyle(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { sv.methodStyle(); };
    }.start();
}


sv.blockStyle() 以私有成员ObjA为对象锁
+ sv.methodStyle() 同步成员方法
输出(无竞争):
blockStyle:0    |Thread-0
methodStyle:0   |Thread-1
blockStyle:1    |Thread-0
methodStyle:1   |Thread-1
methodStyle:2   |Thread-1
blockStyle:2    |Thread-0

线程一调用方法改为sv.blockOneself() 以this为对象锁
sv.blockOneself() + sv.methodStyle()输出(竞争):
blockOneself:0  |Thread-0
blockOneself:1  |Thread-0
blockOneself:2  |Thread-0
methodStyle:0   |Thread-1
methodStyle:1   |Thread-1
methodStyle:2   |Thread-1

  当线程进入某个对象的同步方法时,实际上是拿这个对象的内置锁作为对象锁。(详情见场景二:synchronized修饰方法
  blockOneself()以this为锁锁住代码块,等同于拿当前对象的内置锁作为对象锁。所以当其他线程再进入这个对象的blockOneself()时,因为第一个线程已经独占了对象锁,随后的线程只能阻塞。


d. 验证现象四
  在这里使用类SameObjLockVerify来验证,类定义如下:

public class SameObjLockVerify {
    public static Object staticObj = new Object();
    public void objLock(Object obj){
        synchronized (obj) { track("objLock");}
    }

    public void staticObjLock(){
        synchronized (SameObjLockVerify.staticObj) { track("staticObjLock");}
    }


    /**循环输出一段内容**/
    private static void track(String callerName){
        for(int i = 0;i < 3 ;i++)
            System.out.println(
                    callerName+":"+i+"\t|"+
                    Thread.currentThread().getName());

            Thread.yield();
    }
}

  如果有若干个不同的对象,这些对象中没有一个同步代码块使用与其他对象相同的对象锁。那么多线程访问这些对象,线程间无论如何都不会相互影响。(自行验证)
  这里反向模拟现象四,让我们来看看同一个对象obj作对象锁时,不同线程进入不同对象solv1、solv2时有无竞争?

  为上述SameObjLockVerify类添加如下main方法:

public static void main(String[] args){
    final SameObjLockVerify solv1 = new SameObjLockVerify();
    final SameObjLockVerify solv2 = new SameObjLockVerify();
    final Object objLock = new Object();

    new Thread(){//定为线程一
        public void run() { solv1.objLock(objLock); };
    }.start();
    new Thread(){//定为线程二
        public void run() { solv2.objLock(objLock); };
    }.start();
}


solv1.objLock(objLock)+solv2.objLock(objLock)输出(竞争):
objLock:0   |Thread-0
objLock:1   |Thread-0
objLock:2   |Thread-0
objLock:0   |Thread-1
objLock:1   |Thread-1
objLock:2   |Thread-1

或者这样:

public static void main(String[] args){
    final SameObjLockVerify solv1 = new SameObjLockVerify();
    final SameObjLockVerify solv2 = new SameObjLockVerify();        
    new Thread(){//定为线程一
        public void run() { solv1.staticObjLock(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { solv2.staticObjLock(); };
    }.start();
}


solv1.staticObjLock() + solv2.staticObjLock()输出(竞争):
staticObjLock:0 |Thread-0
staticObjLock:1 |Thread-0
staticObjLock:2 |Thread-0
staticObjLock:0 |Thread-1
staticObjLock:1 |Thread-1
staticObjLock:2 |Thread-1

  即使不是同一个类对象,只要synchronized(对象锁){}使用了相同的对象锁,就会造成多线程的资源竞争。


二、synchronized(Class对象) — 类锁

类锁:是相对于对象锁而抽象出来的一种独占排他锁。当线程拿到一个类A的类锁时,其他线程无法访问以类A为锁(A.class)的同步代码块,也无法进入类A的所有静态同步方法

  类锁其实并不真实存在。所谓类锁,实际上是限制对象仅为Class类对象的对象锁。因此当我们以类A为类锁锁定类A,以类A对象a为对象锁锁定对象a时,两个线程分别访问类A锁定的代码块、对象a锁定的代码块时,并不会产生竞争。(现象五)

a.验证现象五
依旧使用类SynchronizedVerify验证,为其添加如下main方法:

public static void main(String[] args){
    final SynchronizedVerify sv = new SynchronizedVerify();

    new Thread(){//定为线程一
        public void run() { sv.blockOneself(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { sv.blockClass();  };
    }.start();
}


sv.blockOneself() + sv.blockClass()输出(无竞争):
blockOneself:0  |Thread-0
blockClass:0    |Thread-1
blockClass:1    |Thread-1
blockClass:2    |Thread-1
blockOneself:1  |Thread-0
blockOneself:2  |Thread-0

  本文的第一部分,已经验证了在synchronized包裹代码块的场景下,若干同步代码块只要synchronized(对象){}设定的锁相同,多线程进入这若干同步代码块就会产生竞争。既然类锁是特殊的对象锁,那么在此便不验证这点。
  线程进入同步成员方法,需要申请的是该同步成员方法对应对象的内置锁作为对象锁。而线程进入同步静态方法,需要申请的是该类对应的Class类对象的内置锁。这使得synchronized(A.class){}代码块 与 A类的同步静态方法前后被多线程访问时,需要申请同一个锁,于是便产生了竞争。(现象六)

b.验证现象六
SynchronizedVerify类添加如下main()方法:

public static void main(String[] args){
    final SynchronizedVerify sv = new SynchronizedVerify();

    new Thread(){//定为线程一
        public void run() { sv.blockClass(); };
    }.start();
    new Thread(){//定为线程二
        public void run() { SynchronizedVerify.methodStatic(); };
    }.start();
}


sv.blockClass() + SynchronizedVerify.methodStatic()输出(竞争):
blockClass:0    |Thread-0
blockClass:1    |Thread-0
blockClass:2    |Thread-0
methodStatic:0  |Thread-1
methodStatic:1  |Thread-1
methodStatic:2  |Thread-1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值