Synchronized(***) - 锁什么?

今天看synchronized的时候,突然想起它需要注意的一个地方:它的参数值有几个,比如说this、class、任意的对象、用它修饰方法,这都有啥区别?想着以后把它的注意事项给忘了的时候能翻翻写的东西记起来,先记录一下自己懂得的一些地方


锁在Android代码中很常用,因为涉及到异步线程的安全问题,所以要加synchronized来控制代码逻辑的同步执行,但是它的作用又很广泛,这就使得它的参数要有很多个才能满足各种各样的需求。我常常在使用的时候用着用着就懵逼了,汗~~下面挨个看看每个参数的具体用法,顺便复习一遍。


  • 前期准备工作

  • 创建一个class类用来测试

/**
 * 测试synchronized()的类
 */
public class SynchronizedTestBean {

    public void methodA(){
    }

    public void methodB(){
    }
}
  • 再开启两个测试子线程,模拟代码逻辑的异步执行
public class SynchronizedThreadA extends Thread{
    private SynchronizedTestBean bean;

    public SynchronizedThreadA(SynchronizedTestBean bean) {
        super();
        this.bean = bean;
    }
    @Override
    public void run() {
        bean.methodA();
    }
}
public class SynchronizedThreadB extends Thread{
    private SynchronizedTestBean bean;

    public SynchronizedThreadB(SynchronizedTestBean bean) {
        super();
        this.bean = bean;
    }
    @Override
    public void run() {
        bean.methodB();
    }
}

synchronized(*.class)

一句话:锁class,锁所有对象

我们把上述SynchronizedTestBean代码改造一下:

/**
 * 测试synchronized(*.class)的类
 */
public class SynchronizedTestBean {

    public void methodA(){
        try {
            synchronized (SynchronizedTestBean.class) {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                //让该线程睡个3秒再执行
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void methodB(){
        synchronized (SynchronizedTestBean.class) {
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
        }
    }
}

调用它:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-5开始执行methodA times:1521687870408
E/eeeee: 线程名称:Thread-5执行结束methodA times:1521687873408
E/eeeee: 线程名称:Thread-6开始执行methodB times:1521687873410
E/eeeee: 线程名称:Thread-6执行结束methodB times:1521687873410

结论呢,就是如果使用synchronized锁了一个class,那么,这个锁就会对这个class生成的所有对象起作用。
你可能会有疑问,你就生成了一个SynchronizedTestBean对象mBeanOne ,传递给了两个子线程使用,这样也不能有力证明该结论呀。嘿嘿,我懒嘛!
好,再改造下代码:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;
    private SynchronizedTestBean mBeanTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();
        mBeanTwo = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
        threadB.start();
    }
}

这样总可以了吧,看下运行结果

E/eeeee: 线程名称:Thread-7开始执行methodA times:1521689964011
E/eeeee: 线程名称:Thread-7执行结束methodA times:1521689967012
E/eeeee: 线程名称:Thread-8开始执行methodB times:1521689967013
E/eeeee: 线程名称:Thread-8执行结束methodB times:1521689967013

怎么样,是不是threadA在持有mBeanOne对象时,尽管threadB持有不同的mBeanTwo对象,但是两个对象都是SynchronizedTestBean类对象,threadB也只能无奈的等待threadA执行完synchronized(.class)修饰的方法后,才轮到它执行synchronized(.class)方法

synchronized锁静态方法

一句话:锁静态,锁所有对象

我们再把上述SynchronizedTestBean代码改造一下:

/**
 * 测试synchronized静态方法的类
 */
public class SynchronizedTestBean {

    public synchronized static void methodA(){
        try {
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
            //让该线程睡个3秒再执行
            Thread.sleep(3000);
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized static void methodB(){
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
    }
}

因为是静态的方法了,把SynchronizedThreadA、SynchronizedThreadB也改造下吧:

public class SynchronizedThreadA extends Thread{

    @Override
    public void run() {
        SynchronizedTestBean.methodA();
    }
}
public class SynchronizedThreadB extends Thread{

    @Override
    public void run() {
        SynchronizedTestBean.methodB();
    }
}

调用它:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SynchronizedThreadA threadA = new SynchronizedThreadA();
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB();
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-5开始执行methodA times:1521690997803
E/eeeee: 线程名称:Thread-5执行结束methodA times:1521691000803
E/eeeee: 线程名称:Thread-6开始执行methodB times:1521691000804
E/eeeee: 线程名称:Thread-6执行结束methodB times:1521691000804

可以看到,和上面的锁class结果一样,所以,你如果看到一个静态方法被synchronized修饰,你就可以理解成就是对当前对应的*.Class进行加锁

synchronized(this)

一句话:锁this,锁自己

我们再再把上述SynchronizedTestBean代码改造一下:

/**
 * 测试synchronized this的类
 */
public class SynchronizedTestBean {

    public void methodA(){
        try {
            synchronized (this) {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void methodB(){
        synchronized (this) {
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
        }
    }
}

SynchronizedThreadA、SynchronizedThreadB不用修改了,继续沿用最上面的定义

调用它:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
        threadB.start();
    }
}

运行结果如下:

E/eeeee: 线程名称:Thread-5开始执行methodA times:1521697441283
E/eeeee: 线程名称:Thread-5执行结束methodA times:1521697444283
E/eeeee: 线程名称:Thread-6开始执行methodB times:1521697444284
E/eeeee: 线程名称:Thread-6执行结束methodB times:1521697444284

这个结论咱们先别急着下,因为我只创建了一个SynchronizedTestBean对象mBeanOne,接下来我们创建两个对象试试:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;
    private SynchronizedTestBean mBeanTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();
        mBeanTwo = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
        threadB.start();
    }
}

运行结果如下:

E/eeeee: 线程名称:Thread-882开始执行methodA times:1521698553995
E/eeeee: 线程名称:Thread-883开始执行methodB times:1521698554000
E/eeeee: 线程名称:Thread-883执行结束methodB times:1521698554000
E/eeeee: 线程名称:Thread-882执行结束methodA times:1521698556996

可以看到,synchronized(this)只作用一个对象mBeanOne时,锁是有作用的,methodA执行了mBeanOne的synchronized(this)方法,那么methodB就得乖乖等着methodA执行完后才轮到它执行synchronized(this)修饰的方法。但如果有两个对象,则互不影响,methodA执行mBeanOne对象方法时,methodB完全可以执行它的mBeanTwo对象方法,两个互不影响。

synchronized锁普通方法

一句话:锁普通,锁自己

我们再再再把上述SynchronizedTestBean代码改造一下:

/**
 * 测试synchronized普通方法的类
 */
public class SynchronizedTestBean {

    public synchronized void methodA(){
        try {
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
            Thread.sleep(3000);
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodB(){
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
    }
}

调用它:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-1487开始执行methodA times:1521699765485
E/eeeee: 线程名称:Thread-1487执行结束methodA times:1521699768485
E/eeeee: 线程名称:Thread-1488开始执行methodB times:1521699768486
E/eeeee: 线程名称:Thread-1488执行结束methodB times:1521699768486

和上面的synchronized(this)一样
再来看两个对象的情况:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;
    private SynchronizedTestBean mBeanTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();
        mBeanTwo = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-1697开始执行methodA times:1521700056969
E/eeeee: 线程名称:Thread-1698开始执行methodB times:1521700056975
E/eeeee: 线程名称:Thread-1698执行结束methodB times:1521700056975
E/eeeee: 线程名称:Thread-1697执行结束methodA times:1521700059969

也和上面的synchronized(this)一样,所以,你如果看到一个普通方法被synchronized修饰,你就可以理解成就是对当前对应的this进行加锁

synchronized(任意对象)

一句话:锁任意,锁自己
我们再再再再把上述SynchronizedTestBean代码改造一下:

/**
 * 测试synchronized任意对象的类
 */
public class SynchronizedTestBean {

    private String lockStr = new String();

    public synchronized void methodA(){
        try {
            synchronized (lockStr){
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "执行结束methodA times:" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

SynchronizedThreadA、SynchronizedThreadB改造为:

public class SynchronizedThreadA extends Thread{
    private SynchronizedTestBean bean;

    public SynchronizedThreadA(SynchronizedTestBean bean) {
        super();
        this.bean = bean;
    }

    @Override
    public void run() {
        bean.methodA();
    }
}
public class SynchronizedThreadB extends Thread{
    private SynchronizedTestBean bean;

    public SynchronizedThreadB(SynchronizedTestBean bean) {
        super();
        this.bean = bean;
    }

    @Override
    public void run() {
        bean.methodA();
    }
}

调用它:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-1948开始执行methodA times:1521701324345
E/eeeee: 线程名称:Thread-1948执行结束methodA times:1521701327346
E/eeeee: 线程名称:Thread-1947开始执行methodA times:1521701327346
E/eeeee: 线程名称:Thread-1947执行结束methodA times:1521701330347

可以看到,synchronized(lockStr)在作用一个对象mBeanOne时,锁是有用的。threadA先执行synchronized(lockStr)方法,threadB一直在等待threadA释放synchronized(lockStr)方法,下面看看作用在两个对象时的结果:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;
    private SynchronizedTestBean mBeanTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();
        mBeanTwo = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-3482开始执行methodA times:1521702941914
E/eeeee: 线程名称:Thread-3483开始执行methodA times:1521702941917
E/eeeee: 线程名称:Thread-3482执行结束methodA times:1521702944915
E/eeeee: 线程名称:Thread-3483执行结束methodA times:1521702944917

OK,两个对象无效,这不就是和上面的synchronized(this)一样嘛。其实这么测试还不大严谨,你们大可以试试把lockStr对象定义在其他的地方,现在lockStr是全局变量,把它弄成局部变量啥的,多试试,那样才严谨,不过我比较懒嘛,直接给出结论啦,嘿嘿嘿~~

  • 奥,对了,有一点千万千万别搞混了,特做一下说明
    再委屈一下SynchronizedTestBean类,因为还要修改它最后一次:
public class SynchronizedTestBean {

    public void methodA(){
        try {
            synchronized (SynchronizedTestBean.class) {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                //让该线程睡个3秒再执行
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void methodB(){
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
        Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
    }
}

直接调用:

public class MainActivity extends AppCompatActivity {

    private SynchronizedTestBean mBeanOne;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeanOne = new SynchronizedTestBean();

        SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
        threadA.start();

        SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
        threadB.start();
    }
}

运行结果:

E/eeeee: 线程名称:Thread-4497开始执行methodA times:1521703652607
E/eeeee: 线程名称:Thread-4498开始执行methodB times:1521703652611
E/eeeee: 线程名称:Thread-4498执行结束methodB times:1521703652611
E/eeeee: 线程名称:Thread-4497执行结束methodA times:1521703655607

看,synchronized只会影响被它所修饰的方法methodA,methodB没有上锁,所以即使threadA线程在使用mBeanOne对象的methodA方法,这并不排斥threadB使用mBeanOne对象的methodB方法,因为methodB方法没上锁!


Synchronized有很多很多要注意的地方,它的参数只是其中的一小部分。希望在以后慢慢的学习,慢慢的精通,然后再慢慢的补充完善它吧,讲究循序渐进嘛~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,137评论 11 349
  • 简介 本次主要介绍java多线程中的同步,也就是如何在java语言中写出线程安全的程序。如何在java语言中解决非...
    小人物灌篮阅读 466评论 0 1
  • 文:筠心 图:网络(《聊斋志异》特种邮票) 我们常常喜欢说:“人非草木,孰能无情?”仿佛作为“人”这种高级动物,我...
    筠心_阅读 1,745评论 4 17
  • 亲爱的爸爸妈妈: 你们好!“慈母手中线,游子身上衣”,每当读到这些诗...
    118d415ceb4a阅读 207评论 0 0
  • 窗外秋雨夜来访,点点阵阵桂花香。雨打花落风吹月,夜葬花魂湿衣裳。
    阿杜阿云阅读 197评论 0 1