美文网首页Java面试
Synchronized(***) - 锁什么?

Synchronized(***) - 锁什么?

作者: SHERLOCKvv | 来源:发表于2018-03-22 15:33 被阅读9次

    今天看synchronized的时候,突然想起它需要注意的一个地方:它的参数值有几个,比如说this、class、任意的对象、用它修饰方法,这都有啥区别?想着以后把它的注意事项给忘了的时候能翻翻写的东西记起来,先记录一下自己懂得的一些地方


    锁在Android代码中很常用,因为涉及到异步线程的安全问题,所以要加synchronized来控制代码逻辑的同步执行,但是它的作用又很广泛,这就使得它的参数要有很多个才能满足各种各样的需求。我常常在使用的时候用着用着就懵逼了,汗~~下面挨个看看每个参数的具体用法,顺便复习一遍。


    • 前期准备工作

    • 创建一个class类用来测试

    /**
     * 测试synchronized()的类
     */
    public class SynchronizedTestBean {
    
        public void methodA(){
        }
    
        public void methodB(){
        }
    }
    
    • 再开启两个测试子线程,模拟代码逻辑的异步执行
    public class SynchronizedThreadA extends Thread{
        private SynchronizedTestBean bean;
    
        public SynchronizedThreadA(SynchronizedTestBean bean) {
            super();
            this.bean = bean;
        }
        @Override
        public void run() {
            bean.methodA();
        }
    }
    
    public class SynchronizedThreadB extends Thread{
        private SynchronizedTestBean bean;
    
        public SynchronizedThreadB(SynchronizedTestBean bean) {
            super();
            this.bean = bean;
        }
        @Override
        public void run() {
            bean.methodB();
        }
    }
    

    synchronized(*.class)

    一句话:锁class,锁所有对象

    我们把上述SynchronizedTestBean代码改造一下:

    /**
     * 测试synchronized(*.class)的类
     */
    public class SynchronizedTestBean {
    
        public void methodA(){
            try {
                synchronized (SynchronizedTestBean.class) {
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                    //让该线程睡个3秒再执行
                    Thread.sleep(3000);
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void methodB(){
            synchronized (SynchronizedTestBean.class) {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
            }
        }
    }
    
    

    调用它:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-5开始执行methodA times:1521687870408
    E/eeeee: 线程名称:Thread-5执行结束methodA times:1521687873408
    E/eeeee: 线程名称:Thread-6开始执行methodB times:1521687873410
    E/eeeee: 线程名称:Thread-6执行结束methodB times:1521687873410
    

    结论呢,就是如果使用synchronized锁了一个class,那么,这个锁就会对这个class生成的所有对象起作用。
    你可能会有疑问,你就生成了一个SynchronizedTestBean对象mBeanOne ,传递给了两个子线程使用,这样也不能有力证明该结论呀。嘿嘿,我懒嘛!
    好,再改造下代码:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
        private SynchronizedTestBean mBeanTwo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
            mBeanTwo = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
            threadB.start();
        }
    }
    

    这样总可以了吧,看下运行结果

    E/eeeee: 线程名称:Thread-7开始执行methodA times:1521689964011
    E/eeeee: 线程名称:Thread-7执行结束methodA times:1521689967012
    E/eeeee: 线程名称:Thread-8开始执行methodB times:1521689967013
    E/eeeee: 线程名称:Thread-8执行结束methodB times:1521689967013
    

    怎么样,是不是threadA在持有mBeanOne对象时,尽管threadB持有不同的mBeanTwo对象,但是两个对象都是SynchronizedTestBean类对象,threadB也只能无奈的等待threadA执行完synchronized(.class)修饰的方法后,才轮到它执行synchronized(.class)方法

    synchronized锁静态方法

    一句话:锁静态,锁所有对象

    我们再把上述SynchronizedTestBean代码改造一下:

    /**
     * 测试synchronized静态方法的类
     */
    public class SynchronizedTestBean {
    
        public synchronized static void methodA(){
            try {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                //让该线程睡个3秒再执行
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized static void methodB(){
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
        }
    }
    

    因为是静态的方法了,把SynchronizedThreadA、SynchronizedThreadB也改造下吧:

    public class SynchronizedThreadA extends Thread{
    
        @Override
        public void run() {
            SynchronizedTestBean.methodA();
        }
    }
    
    public class SynchronizedThreadB extends Thread{
    
        @Override
        public void run() {
            SynchronizedTestBean.methodB();
        }
    }
    

    调用它:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            SynchronizedThreadA threadA = new SynchronizedThreadA();
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB();
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-5开始执行methodA times:1521690997803
    E/eeeee: 线程名称:Thread-5执行结束methodA times:1521691000803
    E/eeeee: 线程名称:Thread-6开始执行methodB times:1521691000804
    E/eeeee: 线程名称:Thread-6执行结束methodB times:1521691000804
    

    可以看到,和上面的锁class结果一样,所以,你如果看到一个静态方法被synchronized修饰,你就可以理解成就是对当前对应的*.Class进行加锁

    synchronized(this)

    一句话:锁this,锁自己

    我们再再把上述SynchronizedTestBean代码改造一下:

    /**
     * 测试synchronized this的类
     */
    public class SynchronizedTestBean {
    
        public void methodA(){
            try {
                synchronized (this) {
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                    Thread.sleep(3000);
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void methodB(){
            synchronized (this) {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
            }
        }
    }
    

    SynchronizedThreadA、SynchronizedThreadB不用修改了,继续沿用最上面的定义

    调用它:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
            threadB.start();
        }
    }
    

    运行结果如下:

    E/eeeee: 线程名称:Thread-5开始执行methodA times:1521697441283
    E/eeeee: 线程名称:Thread-5执行结束methodA times:1521697444283
    E/eeeee: 线程名称:Thread-6开始执行methodB times:1521697444284
    E/eeeee: 线程名称:Thread-6执行结束methodB times:1521697444284
    

    这个结论咱们先别急着下,因为我只创建了一个SynchronizedTestBean对象mBeanOne,接下来我们创建两个对象试试:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
        private SynchronizedTestBean mBeanTwo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
            mBeanTwo = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
            threadB.start();
        }
    }
    

    运行结果如下:

    E/eeeee: 线程名称:Thread-882开始执行methodA times:1521698553995
    E/eeeee: 线程名称:Thread-883开始执行methodB times:1521698554000
    E/eeeee: 线程名称:Thread-883执行结束methodB times:1521698554000
    E/eeeee: 线程名称:Thread-882执行结束methodA times:1521698556996
    

    可以看到,synchronized(this)只作用一个对象mBeanOne时,锁是有作用的,methodA执行了mBeanOne的synchronized(this)方法,那么methodB就得乖乖等着methodA执行完后才轮到它执行synchronized(this)修饰的方法。但如果有两个对象,则互不影响,methodA执行mBeanOne对象方法时,methodB完全可以执行它的mBeanTwo对象方法,两个互不影响。

    synchronized锁普通方法

    一句话:锁普通,锁自己

    我们再再再把上述SynchronizedTestBean代码改造一下:

    /**
     * 测试synchronized普通方法的类
     */
    public class SynchronizedTestBean {
    
        public synchronized void methodA(){
            try {
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                Thread.sleep(3000);
                Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized void methodB(){
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
        }
    }
    

    调用它:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-1487开始执行methodA times:1521699765485
    E/eeeee: 线程名称:Thread-1487执行结束methodA times:1521699768485
    E/eeeee: 线程名称:Thread-1488开始执行methodB times:1521699768486
    E/eeeee: 线程名称:Thread-1488执行结束methodB times:1521699768486
    

    和上面的synchronized(this)一样
    再来看两个对象的情况:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
        private SynchronizedTestBean mBeanTwo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
            mBeanTwo = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-1697开始执行methodA times:1521700056969
    E/eeeee: 线程名称:Thread-1698开始执行methodB times:1521700056975
    E/eeeee: 线程名称:Thread-1698执行结束methodB times:1521700056975
    E/eeeee: 线程名称:Thread-1697执行结束methodA times:1521700059969
    

    也和上面的synchronized(this)一样,所以,你如果看到一个普通方法被synchronized修饰,你就可以理解成就是对当前对应的this进行加锁

    synchronized(任意对象)

    一句话:锁任意,锁自己
    我们再再再再把上述SynchronizedTestBean代码改造一下:

    /**
     * 测试synchronized任意对象的类
     */
    public class SynchronizedTestBean {
    
        private String lockStr = new String();
    
        public synchronized void methodA(){
            try {
                synchronized (lockStr){
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                    Thread.sleep(3000);
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "执行结束methodA times:" + System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    SynchronizedThreadA、SynchronizedThreadB改造为:

    public class SynchronizedThreadA extends Thread{
        private SynchronizedTestBean bean;
    
        public SynchronizedThreadA(SynchronizedTestBean bean) {
            super();
            this.bean = bean;
        }
    
        @Override
        public void run() {
            bean.methodA();
        }
    }
    
    public class SynchronizedThreadB extends Thread{
        private SynchronizedTestBean bean;
    
        public SynchronizedThreadB(SynchronizedTestBean bean) {
            super();
            this.bean = bean;
        }
    
        @Override
        public void run() {
            bean.methodA();
        }
    }
    

    调用它:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-1948开始执行methodA times:1521701324345
    E/eeeee: 线程名称:Thread-1948执行结束methodA times:1521701327346
    E/eeeee: 线程名称:Thread-1947开始执行methodA times:1521701327346
    E/eeeee: 线程名称:Thread-1947执行结束methodA times:1521701330347
    

    可以看到,synchronized(lockStr)在作用一个对象mBeanOne时,锁是有用的。threadA先执行synchronized(lockStr)方法,threadB一直在等待threadA释放synchronized(lockStr)方法,下面看看作用在两个对象时的结果:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
        private SynchronizedTestBean mBeanTwo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
            mBeanTwo = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanTwo);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-3482开始执行methodA times:1521702941914
    E/eeeee: 线程名称:Thread-3483开始执行methodA times:1521702941917
    E/eeeee: 线程名称:Thread-3482执行结束methodA times:1521702944915
    E/eeeee: 线程名称:Thread-3483执行结束methodA times:1521702944917
    

    OK,两个对象无效,这不就是和上面的synchronized(this)一样嘛。其实这么测试还不大严谨,你们大可以试试把lockStr对象定义在其他的地方,现在lockStr是全局变量,把它弄成局部变量啥的,多试试,那样才严谨,不过我比较懒嘛,直接给出结论啦,嘿嘿嘿~~

    • 奥,对了,有一点千万千万别搞混了,特做一下说明
      再委屈一下SynchronizedTestBean类,因为还要修改它最后一次:
    public class SynchronizedTestBean {
    
        public void methodA(){
            try {
                synchronized (SynchronizedTestBean.class) {
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName() + "开始执行methodA times:" + System.currentTimeMillis());
                    //让该线程睡个3秒再执行
                    Thread.sleep(3000);
                    Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodA times:"+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void methodB(){
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"开始执行methodB times:"+System.currentTimeMillis());
            Log.e("eeeee", "线程名称:" + Thread.currentThread().getName()+"执行结束methodB times:"+System.currentTimeMillis());
        }
    }
    

    直接调用:

    public class MainActivity extends AppCompatActivity {
    
        private SynchronizedTestBean mBeanOne;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBeanOne = new SynchronizedTestBean();
    
            SynchronizedThreadA threadA = new SynchronizedThreadA(mBeanOne);
            threadA.start();
    
            SynchronizedThreadB threadB = new SynchronizedThreadB(mBeanOne);
            threadB.start();
        }
    }
    

    运行结果:

    E/eeeee: 线程名称:Thread-4497开始执行methodA times:1521703652607
    E/eeeee: 线程名称:Thread-4498开始执行methodB times:1521703652611
    E/eeeee: 线程名称:Thread-4498执行结束methodB times:1521703652611
    E/eeeee: 线程名称:Thread-4497执行结束methodA times:1521703655607
    

    看,synchronized只会影响被它所修饰的方法methodA,methodB没有上锁,所以即使threadA线程在使用mBeanOne对象的methodA方法,这并不排斥threadB使用mBeanOne对象的methodB方法,因为methodB方法没上锁!


    Synchronized有很多很多要注意的地方,它的参数只是其中的一小部分。希望在以后慢慢的学习,慢慢的精通,然后再慢慢的补充完善它吧,讲究循序渐进嘛~

    相关文章

      网友评论

        本文标题:Synchronized(***) - 锁什么?

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