目录
前言
synchronized可以为任意对象加锁,用法比较灵活,语法如下
(1)修饰代码块,作用于调用的对象;
(2)修饰方法,作用于调用的对象;
(3)修饰静态方法,作用于所有对象;
(4)修饰类,作用于所有对象。
synchronized取得的锁都是对象锁,而不是把一段代码(或者方法)当成锁!
使用synchronized时,应该注意以下细节问题:
1、synchronized锁的重入性
如下列代码所示,类似于ReentrantLock
在method1中没有释放锁的情况下,可以继续调用synchronized修饰的method2
public class SyncDubbo {
public synchronized void method1(){
System.out.println("method1...");
method2();
}
public synchronized void method2(){
System.out.println("method2...");
method3();
}
public synchronized void method3(){
System.out.println("method1...");
}
public static void main(String[] args) {
final SyncDubbo sd = new SyncDubbo();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
sd.method1();
}
}, "t1");
t1.start();
}
}
如下列代码所示,在子类与父类之间相互调用也运用了synchronized的重用性
public class FatherSon {
static class Father {
public int num = 10;
public synchronized void method1(){
try {
num --;
System.out.println("Father num = " + num);
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
}
}
static class Son extends Father {
public synchronized void method2(){
try {
while (num >0) {
num --;
System.out.println("Son num = " + num);
Thread.sleep(100);
this.method1();
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
Son son = new Son();
son.method2();
}
}, "t1");
t1.start();
}
}
2、不要使用字符串常量作为锁
如下列代码所示,使用了字符串常量作为锁,那么t1和t2运行之后将会一直在t1中出现死循环,t2永远拿不到锁!
解决:可以使用 new String("字符串常量") 作为锁
public class StringLock {
public void method(){
synchronized("字符串常量"){
try {
while (true){
System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
final StringLock sl = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
sl.method();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
sl.method();
}
}, "t2");
t1.start();
t2.start();
}
}
3、锁对象的改变问题
如下列代码所示,使用synchronized锁住了一个对象,并且在代码里重新new了一个对象,导致锁对象改变。
这样在t1还没有运行完代码的时候,t2就已经可以拿到锁了,显然会出现问题!
解决:不要改变锁对象(但是改变对象的属性对代码不会有影响,比如锁是一个人,那么可以改变这个人的身高、年龄等)
public class ChangeLock {
Object obj = new Object();
public void method(){
synchronized(obj){
try {
System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
//从这里改变锁对象
obj = new Object();
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
final ChangeLock cl = new ChangeLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
cl.method();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
cl.method();
}
}, "t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}