synchronized是Java中的关键字,是一种同步锁。它通常有以下几种用法:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码。修饰代码块所用到的锁又包含以下几种:
- synchronized (this) {...},使用this,作为锁。
- synchronized (obj) {...},使用一个实例化对象作为锁。
- synchronized (xxx.class) {...},使用一个class作为锁。
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法。
- 修改一个静态的方法,其作用的范围是整个静态方法。
通过下面这篇笔记,我们可以学会synchronized 的几种使用方法,以及它们之间的区别。
1、synchronized修饰一个代码块
synchronized修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码。
1.1、synchronized(this){...}修饰代码块
下面看一段使用synchronized(this)的代码:
/**
* synchronized修饰一个代码块:指定锁为this。
* 结果:
* 1、被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
* 2、不同的对象,调用这个方法,不会锁住;
* 3、没有被大括号{}括起来的代码,并不会被锁住。
*/
public void synchronizedPart1() {
Log.i("haha", Thread.currentThread().getName() + "线程,非synchronized部分。");
synchronized (this) {
for (int i = 0; i < 5; i++) {
SystemClock.sleep(100);
Log.i("haha", Thread.currentThread().getName() + "线程,结果:" + i);
}
}
}
- 测试1:实例化一个TestSynchronized对象,使用两个线程调用同一个对象的synchronizedPart1( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart1();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart1();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2700线程,非synchronized部分。
Thread-2701线程,非synchronized部分。
Thread-2700线程,结果:0
Thread-2700线程,结果:1
Thread-2700线程,结果:2
Thread-2700线程,结果:3
Thread-2700线程,结果:4
Thread-2701线程,结果:0
Thread-2701线程,结果:1
Thread-2701线程,结果:2
Thread-2701线程,结果:3
Thread-2701线程,结果:4
- 测试2:实例化两个TestSynchronized对象,使用两个线程分别调用这两个对象的synchronizedPart1( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
final TestSynchronized testSynchronized2 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart1();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized2.synchronizedPart1();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2764线程,非synchronized部分。
Thread-2765线程,非synchronized部分。
Thread-2764线程,结果:0
Thread-2765线程,结果:0
Thread-2765线程,结果:1
Thread-2764线程,结果:1
Thread-2765线程,结果:2
Thread-2764线程,结果:2
Thread-2765线程,结果:3
Thread-2764线程,结果:3
Thread-2765线程,结果:4
Thread-2764线程,结果:4
1.2、synchronized(obj){...}修饰代码块
指定某个实例化对象作为synchronized的锁,下面是使用一个实例化对象作为锁的demo:
说明:零长度的byte数组对象创建起来将比任何对象都经济,查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
//作为锁
private byte[] lock = new byte[0];
/**
* synchronized修饰一个代码块:指定锁为某个对象。
* 结果:
* 1、其作用的范围是大括号{}括起来的代码,作用的对象是调用这个类的对所有对象;
* 2、如果指定为锁的对象是同一个,则会被锁住;如果指定为锁的不是同一个对象,则不会被锁住。
* 3、没有被大括号{}括起来的代码,并不会被锁住。
*/
public void synchronizedPart2() {
Log.i("haha", Thread.currentThread().getName() + "线程,非synchronized部分。");
//锁为obj
synchronized (lock) {
for (int i = 0; i < 5; i++) {
SystemClock.sleep(100);
Log.i("haha", Thread.currentThread().getName() + "线程,结果:" + i);
}
}
}
- 测试1:实例化一个TestSynchronized对象,使用两个线程调用同一个对象的synchronizedPart2( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart2();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart2();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2824线程,非synchronized部分。
Thread-2825线程,非synchronized部分。
Thread-2824线程,结果:0
Thread-2824线程,结果:1
Thread-2824线程,结果:2
Thread-2824线程,结果:3
Thread-2824线程,结果:4
Thread-2825线程,结果:0
Thread-2825线程,结果:1
Thread-2825线程,结果:2
Thread-2825线程,结果:3
Thread-2825线程,结果:4
- 测试2:实例化两个TestSynchronized对象,使用两个线程分别调用这两个对象的synchronizedPart2( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
final TestSynchronized testSynchronized2 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart2();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized2.synchronizedPart2();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2864线程,非synchronized部分。
Thread-2865线程,非synchronized部分。
Thread-2864线程,结果:0
Thread-2865线程,结果:0
Thread-2864线程,结果:1
Thread-2865线程,结果:1
Thread-2864线程,结果:2
Thread-2865线程,结果:2
Thread-2864线程,结果:3
Thread-2865线程,结果:3
Thread-2864线程,结果:4
Thread-2865线程,结果:4
1.3、synchronized(xxx.class){...}修饰代码块
指定某个类的.class作为锁,下面是一个demo:
/**
* synchronized修饰一个代码块:指定锁为某个类。
* 结果:
* 1、其作用的范围是大括号{}括起来的代码,作用的对象是调用这个类的对所有对象;
* 2、不同的对象,调用这个方法,也会锁住。
* 3、没有被大括号{}括起来的代码,并不会被锁住。
*/
public void synchronizedPart3() {
Log.i("haha", Thread.currentThread().getName() + "线程,非synchronized部分。");
synchronized (MainActivity.class) {
for (int i = 0; i < 5; i++) {
SystemClock.sleep(100);
Log.i("haha", Thread.currentThread().getName() + "线程,结果:" + i);
}
}
}
- 测试1:实例化一个TestSynchronized对象,使用两个线程调用同一个对象的synchronizedPart3( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart3();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart3();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2892线程,非synchronized部分。
Thread-2893线程,非synchronized部分。
Thread-2892线程,结果:0
Thread-2892线程,结果:1
Thread-2892线程,结果:2
Thread-2892线程,结果:3
Thread-2892线程,结果:4
Thread-2893线程,结果:0
Thread-2893线程,结果:1
Thread-2893线程,结果:2
Thread-2893线程,结果:3
Thread-2893线程,结果:4
- 测试2:实例化两个TestSynchronized对象,使用两个线程分别调用这两个对象的synchronizedPart3( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
final TestSynchronized testSynchronized2 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedPart3();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized2.synchronizedPart3();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-2956线程,非synchronized部分。
Thread-2957线程,非synchronized部分。
Thread-2956线程,结果:0
Thread-2956线程,结果:1
Thread-2956线程,结果:2
Thread-2956线程,结果:3
Thread-2956线程,结果:4
Thread-2957线程,结果:0
Thread-2957线程,结果:1
Thread-2957线程,结果:2
Thread-2957线程,结果:3
Thread-2957线程,结果:4
synchronized修饰一个方法
synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。
下面是使用synchronized修饰方法的一个demo:
/**
* synchronized修饰一个方法。
* 结果:
* 1、被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
* 2、不同的对象,调用这个方法,不会锁住。
*/
public synchronized void synchronizedMethod() {
for (int i = 0; i < 5; i++) {
SystemClock.sleep(100);
Log.i("haha", Thread.currentThread().getName() + "线程,结果:" + i);
}
}
- 测试1:实例化一个TestSynchronized对象,使用两个线程调用同一个对象的synchronizedMethod( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedMethod();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedMethod();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-3024线程,结果:0
Thread-3024线程,结果:1
Thread-3024线程,结果:2
Thread-3024线程,结果:3
Thread-3024线程,结果:4
Thread-3025线程,结果:0
Thread-3025线程,结果:1
Thread-3025线程,结果:2
Thread-3025线程,结果:3
Thread-3025线程,结果:4
- 测试2:实例化两个TestSynchronized对象,使用两个线程分别调用这两个对象的synchronizedMethod( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
final TestSynchronized testSynchronized2 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedMethod();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized2.synchronizedMethod();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-3072线程,结果:0
Thread-3073线程,结果:0
Thread-3072线程,结果:1
Thread-3073线程,结果:1
Thread-3072线程,结果:2
Thread-3073线程,结果:2
Thread-3072线程,结果:3
Thread-3073线程,结果:3
Thread-3072线程,结果:4
Thread-3073线程,结果:4
synchronized修饰一个静态方法
下面的demo是使用synchronized来修饰一个静态方法:
/**
* synchronized修饰一个静态方法。
* 结果:
* 1、其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
* 2、不同的对象,调用这个方法,也会锁住。
*/
public synchronized static void synchronizedStaticMethod() {
for (int i = 0; i < 5; i++) {
SystemClock.sleep(100);
Log.i("haha", Thread.currentThread().getName() + "线程,结果:" + i);
}
}
- 测试1:实例化一个TestSynchronized对象,使用两个线程调用同一个对象的synchronizedStaticMethod( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedStaticMethod();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedStaticMethod();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-3084线程,结果:0
Thread-3084线程,结果:1
Thread-3084线程,结果:2
Thread-3084线程,结果:3
Thread-3084线程,结果:4
Thread-3085线程,结果:0
Thread-3085线程,结果:1
Thread-3085线程,结果:2
Thread-3085线程,结果:3
Thread-3085线程,结果:4
- 测试2:实例化两个TestSynchronized对象,使用两个线程分别调用这两个对象的synchronizedStaticMethod( )方法:
final TestSynchronized testSynchronized1 = new TestSynchronized();
final TestSynchronized testSynchronized2 = new TestSynchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized1.synchronizedStaticMethod();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
testSynchronized2.synchronizedStaticMethod();
}
});
thread1.start();
thread2.start();
执行结果:
Thread-3088线程,结果:0
Thread-3088线程,结果:1
Thread-3088线程,结果:2
Thread-3088线程,结果:3
Thread-3088线程,结果:4
Thread-3089线程,结果:0
Thread-3089线程,结果:1
Thread-3089线程,结果:2
Thread-3089线程,结果:3
Thread-3089线程,结果:4
总结
- synchronized修饰代码块和修饰方法的区别:
- 修饰代码块作用的范围是大括号{}括起来的代码;
- 修饰方法作用的范围是整个函数。
- synchronized修饰代码块的时候,synchronized(this)、synchronized(obj)、synchronized(xxx.class)的相同点:
- 其作用的范围是大括号{}括起来的代码;
- 没有被大括号{}括起来的代码,并不会被锁住。
- synchronized修饰代码块的时候,synchronized(this)、synchronized(obj)、synchronized(xxx.class)有何不同:
- synchronized(this)指定的锁是当前类的实例化对象。同一个实例化对象,调用同步代码块的时候,会被锁住;不同实例化对象,调用同步代码块的时候,不会被锁住。
- synchronized(obj)指定的锁是指定的某个实例化对象。只要锁相同,哪怕是不同的实例化对象,调用同步代码块的时候,也会被锁住。
- synchronized(xxx.class)指定的锁是xxx.class。无论对象相不相同,只要调用这个同步代码块,就会被锁住。
- synchronized修饰方法,它的锁是什么?
- synchronized修饰方法的时候,它的锁是这个同步方法所在的对象的实例化。
- synchronized修饰方法的时候,同一个实例化对象,调用同步方法的时候,会被锁住;不同对象调用同步方法的时候,不会被锁住。
- synchronized修饰一个静态方法和修饰方法有何不同?
- synchronized修饰一个静态方法,作用范围的是静态方法所属类的所有对象。
- synchronized修饰一个方法,作用范围是这个方法所属对象的实例化。