Java面试

Synchronized(***) - 锁什么?

2018-03-22  本文已影响9人  SHERLOCKvv

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


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


/**
 * 测试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是全局变量,把它弄成局部变量啥的,多试试,那样才严谨,不过我比较懒嘛,直接给出结论啦,嘿嘿嘿~~

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

上一篇下一篇

猜你喜欢

热点阅读