Synchronized-谁锁住了谁

什么是锁,什么锁住了什么

在 Android 中保证线程安全 有一种方式是使用 Synchronized 关键字。使用的是 Object 自带的内在锁( Intrinsic Lock 本质的 固有的 锁)。

Synchronized 的表现形式

同步代码块

   public  void getTicket() {
        synchronized (objectA) {
            System.out.println("code block ");
        }

    }

同步方法

public synchronized void getTicket() {
        //...
    }

Synchronzied 干了啥

synchronized 不管是同步代码块,还是同步方法,内部都是做了一样事,就是告诉别人这个东西是他的了,他动的时候别人都不能动,这样才能保证这个东西的安全。他是谁,他就是线程,别人就是其他线程。这个东西是啥,就是需要保证原子性的代码。怎么告诉别人的,就是在用这个东西的时候加个锁,锁的是啥,锁住的是对象,别的线程一看到这个东西锁住了,就没法用了,就等着。简言之,就是 <b>线程锁住了对象</b>。

举几个例子

在 MultiPassenger 中新建两个 线程,分别去调 ticket 的 getTicket() 和 refundTicket()。

public class MultiPassenger {
    public static void main(String args[]) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                ticket.getTicket();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                ticket.refundTicket();
            }
        }).start();
    }
}
  1. getTicket() refundTicketd() 都不加 Synchronized
public class Ticket {

    public void getTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  get ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void refundTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  refund ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出

1487387908597  get ticket
1487387908598  refund ticket

时间差只有 1ms,可以看出 两个线程执行方法时都没有互相等待。

  1. getTicket() 方法前加 Synchronized,refundTicketd() 不加
 public synchronized  void getTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  get ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void refundTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  refund ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

输出

1487388144889  get ticket
1487388144892  refund ticket

时间差也是只有 3ms,可以看出 两个线程执行方法时也都没有互相等待。

  1. getTicket() 和 refundTicketd() 都加 Synchronized
    public synchronized  void getTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  get ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void refundTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  refund ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

输出

1487388226934  get ticket
1487388227935  refund ticket

时间差大约 1s,可以看出 退票的线程等了取票的线程 1s。前文说 加锁锁住的是对象,在这里锁住的就是当前方法所在的对象。

  1. 改一下入口方法 ,加上锁,锁住 ticket,然后去掉 refundTicket() 前的 Synchronizd。
public class MultiPassenger {
    public static void main(String args[]) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                ticket.getTicket();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ticket){             //这里锁住了 ticket
                    ticket.refundTicket();
                }

            }
        }).start();
    }
}
public class Ticket {

    public synchronized  void getTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  get ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void refundTicket() {
        try {
            System.out.println(System.currentTimeMillis() + "  refund ticket");
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行下,输出

1487389351027  refund ticket
1487389352029  get ticket

时间间隔还是 1s,说明synchronized 加在 方法上锁住的就是当前方法所在的对象实例。

  1. 如果是静态方法上面加 synchronized 呢,锁住的是啥。其实是当前类 this.getClass(),类也是个对象。
public class Ticket {
    public synchronized static void getTicket() {  //静态类加锁
        try {
            System.out.println(System.currentTimeMillis() + "  get ticket");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void refundTicket() {
        try {
            synchronized (getClass()) { //锁住 getClass()
                System.out.println(System.currentTimeMillis() + "  refund ticket");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行下输出

1487389688340  get ticket
1487389689342  refund ticket

时间差了 1s,可以看出静态方法前加 Synchronized 和 Synchronized(this.getClass()) 锁住的是同一个东西。

  1. 再举个死锁的例子玩玩
    如果 Thread1 锁住了 objectA,Thread2 锁住了 objectB,这时 Thread1 还需要去锁 objectB,就需要等 Thread2 执行完成释放objectB,但是这时Thread2 还需要 objectA ,就会发生死锁,谁都过不去。
    代码如下
public class Ticket {
    private Object objectA = new Object();
    private Object objectB = new Object();

    public void getTicket() {
        try {
            synchronized (objectA) {
                Thread.sleep(100);
                System.out.println(Thread.currentThread()+ " getTicket lock a");
                synchronized (objectB) {
                    System.out.println(Thread.currentThread()+ " getTicket lock b");
                    System.out.println(System.currentTimeMillis() + "  get ticket");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(Thread.currentThread()+ "getTicket over");
        }
    }

    public void refundTicket() {
        try {
            synchronized (objectB) {
                System.out.println(Thread.currentThread()+ " refundTicket lock b");
                Thread.sleep(100);
                synchronized (objectA) {
                    System.out.println(Thread.currentThread()+ " refundTicket lock a");
                    System.out.println(System.currentTimeMillis() + "  refund ticket");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(Thread.currentThread()+ " refundTicket over");
        }
    }
}

输出

Thread[Thread-1,5,main] refundTicket lock b
Thread[Thread-0,5,main] getTicket lock a

只输出了两行 log,没有终止,只能手动终止程序。

总结

  • synchronized 是线程锁住了对象。
  • synchronized 加在 非静态方法上,锁住的是 当前对象。
  • 加在静态方法前,锁住的是当前类(this.getclass() 类也是对象)。
  • synchronized(objectA) 加在代码块上,锁住的就是 objectA。
  • 要注意 synchronized 可能引发死锁

致谢

如有错误,望指出,十分感谢。

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

推荐阅读更多精彩内容