美文网首页
synchronized 探究

synchronized 探究

作者: sadamu0912 | 来源:发表于2018-12-09 17:22 被阅读0次

    synchronized 其实说起来很简单,就是每一个对象都有一个monitor对象。在运行到同步方法,或者同步的

    方法块的时候,前面加moniterenter,结束的代码后面接moniterexit。很多人都总结了这么三句话。

    • 对于普通方法同步,锁是当前实例对象
    • 对于静态方法同步,锁是当前类的 Class 对象
    • 对于方法块同步,锁是 Synchronized 括号里的对象
      但是,我不知道这三句话是怎么来的,为什么会这样?所以提出了以下几个问题

    1 Thread1通过同步方法1,访问静态变量A,另外一个线程Thread2 通过同一个变量Obj访问同步方法1,或者同步方法2,访问静态变量,是顺序执行,还是可以并发访问?

    上代码:

    /**
     * 这个实例说明,synchronized并不支持,读写锁的概念,一个线程在读数据的时候,另外一个线程
     * 不能进来
     */
    public class Demo003 {
        //private   int count = 0;
         private  static int count = 0;
    
        /**
         * 一个线程先进来,睡2秒,获取monitor,
         * 然后假如说另外一个线程可以进来,说明多个读线程,可以共存
         * @return
         */
        public synchronized  String getCount() {
            //public synchronized static  vo id add() {
            System.out.println(Thread.currentThread().getName()+">enter=====");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+">getcount="+count);
    
           return new Integer(count).toString();
        }
    
        /**
         * 一个线程先进来,睡2秒,获取monitor,
         * 然后假如说另外一个线程可以进来,说明多个读线程,可以共存
         * @return
         */
        public synchronized  String getCount2() {
            //public synchronized static  void add() {
            System.out.println(Thread.currentThread().getName()+">enter=====");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+">getcount="+count);
    
            return new Integer(count).toString();
        }
    
    
    
        public static void main(String[] args) {
    
    
            final Demo003 obj = new Demo003();
            Thread t1 = new Thread(new Runnable(){
                @Override
                public void run() {
                    obj.getCount(); //1.同一个对象、同一把锁
                }
            }, "thread1");
    
            Thread t2 = new Thread(new Runnable(){
                @Override
                public void run() {
                    //thread1.add();    //1、同一个对象、同一把锁
                    obj.getCount2();
                }
            }, "thread2");
    
            t1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t2.start();
        }
    }
    

    结果是顺序执行。 可见synchronized 无法实现juc的读写锁概念。

    2 假如说我线程Thread1,锁住class对象,然后另外一个线程Thread2,能获得class对象而且执行class对象的方法吗?

    上代码:

    /**
     * 如果一个线程锁住class对象,能通过实例访问class对象吗?
     */
    public class Demo008 {
    
        public   void getCount() {
            synchronized(Demo008.class){
                System.out.println(Thread.currentThread().getName()+">enter=");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+">leave=");
            }
    
        }
    
        public static void main(String[] args) {
    
    
            final Demo008 obj = new Demo008();
            Thread t1 = new Thread(new Runnable(){
                @Override
                public void run() {
                    obj.getCount(); //1.同一个对象、同一把锁
                }
            }, "thread1");
    
            Thread t2 = new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println("获取class对象并且调用class对象的方法");
                    System.out.println(obj.getClass().getName());
                }
            }, "thread2");
    
            t1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t2.start();
        }
    }
    

    结果是锁住class对象,另外的线程可以拿到class对象,并且调用class的方法

    3 假如线程Thread1 访问静态同步方法,另外一个线程Thread2,可以访问静态变量吗?

    上代码:

    /**
     * 这个实例说明,一个线程访问对象的静态同步方法1,另外一个线程通过同一个对象访问静态变量
     * 可以进来
     * 实际结果: System.out.println(obj.count2);//1、通过对象访问静态变量
     *  System.out.println(count2);//1、直接访问静态变量
     *  都是可以的
     *  可以发现带static的同步方法是锁住class对象,如果是不带static是锁住对象
     */
    public class Demo005 {
        private static  int count = 0;
    
        public   static int count2 = 5;
    
        public synchronized static   void getCount() {
            System.out.println(Thread.currentThread().getName()+">enter=");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+">getCount="+count);
            System.out.println(Thread.currentThread().getName()+">leave=");
        }
    
        public static void main(String[] args) {
    
    
            final Demo005 obj = new Demo005();
            Thread t1 = new Thread(new Runnable(){
                @Override
                public void run() {
                    obj.getCount(); //1.同一个对象、同一把锁
                }
            }, "thread1");
    
            Thread t2 = new Thread(new Runnable(){
                @Override
                public void run() {
                    //thread1.add();
                    System.out.println(obj.count2);//1、通过对象访问静态变量
                }
            }, "thread2");
    
            t1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t2.start();
        }
    }
    

    结果是可以,可见锁住class对象和类的静态变量没有关系。同理锁住对象和类的属性,非同步方法获取属性没有关系

    4 还有问题,就是通过前面的文章可以知道,即使cpu内部产生中断,Thread2还是拿不到对象锁。cpu在切换线程的时候,时钟会产生中断,那么是不是可以推断,在线程Thread1拿到对象锁,然后发生异常时,Thread2是不是还是拿不到对象锁?

    上代码:

    /**
     * 如果一个线程锁住对象,能通过实例访问静态变量,或者其他变量
     */
    public class Demo010 {
        private static   Integer count = 0;
    
        public   static Integer count2 = 2;
    
        private Integer count3 = 3;
        public Integer count4 =4;
    
        public Integer getCount3() {
            return count3;
        }
    
        public static void main(String[] args) {
    
    
            final Demo010 obj = new Demo010();
            Thread t1 = new Thread(new Runnable(){
                @Override
                public void run() {
                    synchronized (obj){
                        System.out.println(Thread.currentThread().getName()+">enter=");
                        int k = 10/0;
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+">leave=");
                    }
                }
            }, "thread1");
    
            Thread t2 = new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println("对象被锁住时,并且发生异常");
                    System.out.println(obj.getCount3());
                    System.out.println(obj.count4);
                }
            }, "thread2");
    
            t1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t2.start();
        }
    }
    

    代码输出结果:

    thread1>enter=
    Exception in thread "thread1" java.lang.ArithmeticException: / by zero
        at com.xjx.jdktest.concurrent.Demo010$1.run(Demo010.java:27)
        at java.lang.Thread.run(Thread.java:745)
    对象被锁住时,并且发生异常
    3
    4
    

    结果是,发生异常后,线程会释放对象锁,使得后面的线程可以继续抢占锁。

    5 问题5 synchronized是公平锁还是非公平锁?假如是非公平锁怎么防止活锁,也就是锁饥饿呢?

    上代码:

    import java.util.concurrent.CountDownLatch;
    
    /**
     * synchronized 是非公平锁还是公平锁  5个线程一起启动,3个线程调用synchronized方法,还有一个线程睡了1秒之后
     * 才开始调用ynchronized方法,还有一个线程直接拿对象锁,拿到之后睡30s然后执行同步方法
     */
    public class Demo011 {
        private static CountDownLatch ready = new CountDownLatch(1);
        private static Integer count =1;
        public synchronized void getCount() {
            System.out.println(Thread.currentThread().getName()+">enter getCount=");
            System.out.println(Thread.currentThread().getName()+">getCount="+count);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+">leave getCount=");
        }
        public static void main(String[] args) {
                final Demo011 obj = new Demo011();
                for(int i=0;i<3;i++){
                  new Thread(new DemoThread(ready,obj)).start();
                }
                new Thread(new DemoThread2(ready,obj),"DemoThread2").start();
            new Thread(new DemoThread3(ready,obj),"DemoThread3").start();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                ready.countDown();
        }
    }
    class DemoThread implements Runnable{
        CountDownLatch latch ;
        Demo011 obj;
        DemoThread(CountDownLatch latch,Demo011 obj){
            this.latch = latch;
            this.obj = obj;
        }
        @Override
        public void run() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
                        obj.getCount();
        }
    }
    
    class DemoThread2 implements Runnable{
        CountDownLatch latch ;
        Demo011 obj;
        DemoThread2(CountDownLatch latch,Demo011 obj){
            this.latch = latch;
            this.obj = obj;
        }
        @Override
        public void run() {
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                obj.getCount();
        }
    }
    
    class DemoThread3 implements Runnable{
        CountDownLatch latch ;
        Demo011 obj;
        DemoThread3(CountDownLatch latch,Demo011 obj){
            this.latch = latch;
            this.obj = obj;
        }
        @Override
        public void run() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            synchronized (obj){
                System.out.println(Thread.currentThread().getName()+">getObj=");
                long start = System.currentTimeMillis();
                try {
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                obj.getCount();
                System.out.println("this thread has owned monitor for "+new Long(System.currentTimeMillis()-start).toString()+" ms");
                System.out.println(Thread.currentThread().getName()+">release Obj=");
            }
    
        }
    }
    
    

    输出结果:

    Thread-0>enter getCount=
    Thread-0>getCount=1
    Thread-0>leave getCount=
    DemoThread2>enter getCount=
    DemoThread2>getCount=1
    DemoThread2>leave getCount=
    DemoThread3>getObj=
    DemoThread3>enter getCount=
    DemoThread3>getCount=1
    DemoThread3>leave getCount=
    this thread has owned monitor for 33001 ms
    DemoThread3>release Obj=
    Thread-2>enter getCount=
    Thread-2>getCount=1
    Thread-2>leave getCount=
    Thread-1>enter getCount=
    Thread-1>getCount=1
    Thread-1>leave getCount=
    

    结论: synchronized是非公平锁,而且如果某些线程执行时间特别长的话,会造成锁饥饿

    先写到这里吧,要想看更精彩的内容,请点赞关注吧,请听下回分解。

    相关文章

      网友评论

          本文标题:synchronized 探究

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