synchronize可以在多个线程操作同一个成员变量或者方法时,实现同步(或者互斥)的效果。
synchronized可以作用于方法,以及方法内部的代码块。
//1
synchronized void method(){}
//2
static synchronized void method(){}
//3
synchronized void method(){
synchronized(锁对象){
}
}
//4
static synchronized void method(){
synchronized(锁对象){
}
}
锁对象
那么在上面的示例中,它们分别持有的锁对象是谁?
synchronized作用于非静态方法以及非静态方法内部的代码块,持有的是当前类的对象的锁,并且是同一个锁。作用于静态方法及其内部的代码块,持有的是当前类的Class对象的锁,并且和非静态方法不是同一个锁。
通过代码来验证。
public class SynchronizedTest {
private synchronized void test1(){
for (int x = 0; x < 5; x++) {
System.out.println("test1---"+x);
}
}
private void test2(){
synchronized(this) {
for (int x = 0; x < 5; x++) {
System.out.println("---test2---"+x);
}
}
}
private static synchronized void test3(){
for (int x = 0; x < 5; x++) {
System.out.println("------test3---"+x);
}
}
private static void test4(){
synchronized (SynchronizedTest.class){
for (int x = 0; x < 5; x++) {
System.out.println("---------test4---"+x);
}
}
}
public static void main(String[] args) {
SynchronizedTest synchronizedTest = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedTest.test1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedTest.test2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test3();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test4();
}
}).start();
}
}
执行结果
test1---0
------test3---0
test1---1
------test3---1
test1---2
test1---3
------test3---2
test1---4
------test3---3
------test3---4
---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
---------test4---0
---------test4---1
---------test4---2
---------test4---3
---------test4---4
test1和test2不会交叉执行,test3和test4也不会交叉执行。非静态方法以及方法内部的代码块持有的是同一个对象锁,它们是同步执行的。静态方法和内部的代码块持有的是当前类的Class对象锁,它们是同步执行的。而静态方法和非静态方法持有的不是同一个锁,它们是异步的。
String作为锁
字符串常量作为锁,会有什么结果?
final String a = "100";
final String b = "100";
new Thread(new Runnable() {
@Override
public void run() {
synchronized (a){
while (true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
},"thread-a").start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (b){
while (true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
},"thread-b").start();
这里字符串a和b虽然是两个对象,但是声明b时,会将字符串常量池中已存在的a的值直接赋给b。这样a和b其实是一样的。这样线程thread-a和thread-b同时抢占同一个锁,一旦一个线程抢到该锁,另一个线程就再也获取不到该锁。
synchronized不具有继承性
自己覆盖了父类被synchronized修饰的方法,子类方法如果需要同步性,也需要用synchronized修饰。
定义子类Sub继承自SynchronizedTest
class Sub extends SynchronizedTest{
@Override
public void test2() {
//super.test2();
for (int x = 0; x < 15; x++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
然后开启两个线程调用Sub的test2()方法。
final Sub sub = new Sub();
new Thread(new Runnable() {
@Override
public void run() {
sub.test2();
}
},"Sub---A").start();
new Thread(new Runnable() {
@Override
public void run() {
sub.test2();
}
},"Sub---B").start();
打印结果
Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B
可见Sub的test2()方法并没有同步性。也就是synchronized不能被继承。
可重入锁
使用synchronized时,当一个线程请求一个对象锁时,再次请求该锁是可以立即得到的。
public class SynchronizedTest {
private synchronized void test1(){
for (int x = 0; x < 5; x++) {
System.out.println("test1---"+x);
}
test2();
}
public void test2(){
synchronized(this) {
for (int x = 0; x < 5; x++) {
System.out.println("---test2---"+x);
}
}
}
public static void main(String[] args) {
SynchronizedTest synchronizedTest = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedTest.test1();
}
}).start();
}
}
在方法test1()中调用方法test2(),并不需要等待锁,而是立即获取到锁。
把子类Sub的test2()方法也改成synchronized修饰。并在其内部调用父类的test2()方法。能获得锁吗?
class Sub extends SynchronizedTest{
@Override
public synchronized void test2() {
//调用父类的同步方法
super.test2();
for (int x = 0; x < 15; x++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
打印结果
---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
Sub---A
Sub---A
Sub---A
Sub---A
可以看到父类方法执行完毕,子类的方法立即执行。可见,在子父类的继承关系中,也支持可重入锁这个特性。
出现异常,会自动释放锁
同步方法与同步代码块
synchronized作用于整个方法,可能引起方法执行效率下降。建议将方法内部需要同步的代码用synchronized修饰,也就是synchronized代码块。
多个同步锁对象
在一个类中假如有多个同步方法,它们之间并不需要互斥。那么使用同一个锁,会大大降低效率。可以定义多个同步锁对象。
Object obj1 = new Object();
Object obj2 = new Object();
public void method1(){
synchronized(obj1){
}
}
public void method2(){
synchronized(obj2){
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。