synchronized 探究

2018-12-09  本文已影响0人  sadamu0912

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

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

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是非公平锁,而且如果某些线程执行时间特别长的话,会造成锁饥饿

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

上一篇下一篇

猜你喜欢

热点阅读