Synchronized-谁锁住了谁

作者: FrankFan | 来源:发表于2017-02-18 12:13 被阅读135次

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

    在 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 可能引发死锁

    致谢

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

    相关文章

      网友评论

        本文标题:Synchronized-谁锁住了谁

        本文链接:https://www.haomeiwen.com/subject/kaerwttx.html