synchronized(this/.class/Object)

2018-02-26  本文已影响312人  LPhoenix

synchronized关键字的使用

运行结果:

    02-24 10:05:12.135 6434-7477/com.example.refreshdemo D/HotProductService: A begin time=1519437912141
    02-24 10:05:14.135 6434-7477/com.example.refreshdemo D/HotProductService: A end   time=1519437914142
    02-24 10:05:14.135 6434-7478/com.example.refreshdemo D/HotProductService: B begin time=1519437914145
    02-24 10:05:14.135 6434-7478/com.example.refreshdemo D/HotProductService: B end   time=1519437914146

上面的结果我们能看到即使A业务耗时较长,B业务也并不会获得执行的机会,如果我们将核心业务代码中B业务或者A业务的同步锁去掉,其他照旧会是怎么样

    ...
    比如去掉B业务的同步锁
    public void serviceMethodB(){
    //synchronized (this) 
        {
        Log.d("HotProductService","B begin time="+System.currentTimeMillis());
        Log.d("HotProductService","B end   time="+System.currentTimeMillis());
        }
    }
    ...

运行结果:

    02-24 02:27:19.248 3562-3629/com.example.refreshdemo D/HotProductService: A begin time=1519439239248
    02-24 02:27:19.248 3562-3630/com.example.refreshdemo D/HotProductService: B begin time=1519439239248
    02-24 02:27:19.248 3562-3630/com.example.refreshdemo D/HotProductService: B end   time=1519439239248
    02-24 02:27:21.248 3562-3629/com.example.refreshdemo D/HotProductService: A end   time=1519439241248

结论:synchronized (this)使用的对象监视器是一个,即是该对象自身,当一个线程访问HotProductService的一个synchronized (this)同步代码块时,其它线程对同一个HotProductService中其它的synchronized (this)同步代码块的访问将是堵塞,实现了代码顺序的同步执行。

运行结果:

    02-24 02:48:32.212 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=1
    02-24 02:48:33.213 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=2
    02-24 02:48:34.214 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=3
    02-24 02:48:35.215 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=4
    02-24 02:48:36.216 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=5
    02-24 02:48:37.217 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=6
    02-24 02:48:38.218 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=7
    02-24 02:48:39.219 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=8
    02-24 02:48:40.220 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=9
    02-24 02:48:41.221 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=10
    02-24 02:48:42.223 3562-3764/com.example.refreshdemo D/HotProductService: run----objectMethodA2

同样验证了只能同时被一个线程所访问synchronized代码块。其他线程遇到synchronized代码块将会堵塞。

运行结果:

    02-24 02:57:41.380 3975-4016/? D/HotProductService: thread name=A 进入代码快:1519441061380
    02-24 02:57:44.382 3975-4016/com.example.refreshdemo D/HotProductService: thread name=A 进入代码快:1519441064382入参uname:a入参pwd:aa
    02-24 02:57:44.382 3975-4017/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441064382
    02-24 02:57:47.383 3975-4017/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441067383入参uname:b入参pwd:bb

如果我们将

    //核心业务代码
        public class HotProductService {
            private String uname;
            private String pwd;
           //String lock=new String();移入方法会得到什么样的结果呢
            public void setUserNamePassWord(String userName,String passWord){
                try {
                    String lock=new String();
                    synchronized (lock) {
                        Log.d("HotProductService","thread name=" + Thread.currentThread().getName()
                                + " 进入代码快:" + System.currentTimeMillis());
                        uname=userName;
                        Thread.sleep(3000);
                        pwd=passWord;
                        Log.d("HotProductService","thread name="+Thread.currentThread().getName()
                                +" 进入代码快:"+System.currentTimeMillis()+"入参uname:"+uname+"入参pwd:"+pwd);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

其他代码照旧得到
运行结果:

    02-24 03:04:16.757 4148-4187/? D/HotProductService: thread name=A 进入代码快:1519441456757
    02-24 03:04:16.758 4148-4188/? D/HotProductService: thread name=B 进入代码快:1519441456758
    02-24 03:04:19.758 4148-4187/com.example.refreshdemo D/HotProductService: thread name=A 进入代码快:1519441459758入参uname:b入参pwd:aa
    02-24 03:04:19.759 4148-4188/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441459759入参uname:b入参pwd:bb

是不是很奇怪出错了?为什么呢 移入方法内部就不对了,比较前后改变,发现修改后每次调用方法时(哪个线程调用方法时)都会重新new一个对象作为对象锁,但是修改前不管哪次进入方法时(哪个线程调用方法时)用的都是同一个对象,所以我们得到结论:

使用自定义任意对象进行同步锁 不同线程必须为同一对象,否则仍旧是异步运行的

可以直接给出的结论是这两种方式的同步是一样的,使用的同步锁都是对应的类作为对象锁,在jvm中类是唯一的,那也就是说对任何对象都是同步的,因为具有唯一的对象锁。下面来验证下:

    //核心业务代码
    public class HotProductService {
        //业务A
        public  void methodA(){
            try {
                synchronized (HotProductService.class){
                    Log.d("HotProductService","static methodA begin Name:" + Thread.currentThread().getName() + " times:" + System.currentTimeMillis());
                    Thread.sleep(2000);
                    Log.d("HotProductService","static methodA end   Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //业务B
        public  void methodB(){
            synchronized (HotProductService.class){
                Log.d("HotProductService","static methodB begin Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
                Log.d("HotProductService","static methodB end   Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
            }
        }
    }

    //A线程
    public class ThreadA5 extends Thread{
    
        private  HotProductService mHotProductService;
    
        public ThreadA5(HotProductService hotProductService){
            mHotProductService = hotProductService;
        }
        @Override
        public void run() {
            super.run();
            mHotProductService.methodA();
        }
    }
    
    //B线程
    public class ThreadB5 extends Thread {
    
        private  HotProductService mHotProductService;
    
        public ThreadB5(HotProductService hotProductService){
            mHotProductService = hotProductService;
        }
        @Override
        public void run() {
            super.run();
            mHotProductService.methodB();
        }
    }

    private void test5() {
        HotProductService service= new HotProductService();
        ThreadA5 a=new ThreadA5(service);
        a.start();
        ThreadB5 b=new ThreadB5(service);
        b.start();
    }

运行结果:

    02-24 03:24:11.250 4148-4282/com.example.refreshdemo D/HotProductService: static methodA begin Name:Thread-174 times:1519442651250
    02-24 03:24:13.252 4148-4282/com.example.refreshdemo D/HotProductService: static methodA end   Name:Thread-174 times:1519442653252
    02-24 03:24:13.252 4148-4283/com.example.refreshdemo D/HotProductService: static methodB begin Name:Thread-175 times:1519442653252
    02-24 03:24:13.252 4148-4283/com.example.refreshdemo D/HotProductService: static methodB end   Name:Thread-175 times:1519442653252

使用.class作为对象锁保证了不能线程的同步。
引用网上常见的说法就是:

synchronize修饰的方法和 synchronize(this) 都是锁住自己本身的对象 而synchronize(class) synchronize(object) 都是锁别的对象

上一篇 下一篇

猜你喜欢

热点阅读