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