1.同步锁的基本理解
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。
2.使用时要明白两个问题
- 锁的对象是谁
- 锁当前的持有者
举例子说明:
例一:
public class Test {
public synchronized void methodA(String param) {
System.out.println("methodA's param :" + param);
}
public synchronized void methodB(){
methodA("methodB");
}
public static void main(String[] args) {
Test test = new Test();
test.methodB();
}
}
执行结果:
methodA’s param :methodB invoke
分析:
① methodB被调用因为有synchronized需要获得一个锁,锁的对象是test,也就是当前Test类的实例,而获得所得东西是线程,也就是当前线程拿到了test的锁(不是B方法获得了锁)
② methodB内部调用了methodA,此时A也加了synchronized,也需要获得一个锁,因为A和B都是Test类中的方法,所以当前线程要获得的锁的对象也是test,而当前线程执行B方法时已经持有了test对象的锁,所以会直接调用methodA,相当于方法A上没有加synchronized。
例二:
public class Test {
public synchronized void methodA(String param) {
System.out.println("methodA's param :" + param);
}
public synchronized void methodB(){
methodA("methodB");
}
public static void main(String[] args) {
Test test1 = new Test();
Test test2 = new Test();
test1.methodB();
test2.methodB();
}
}
运行结果:
methodA’s param :methodB
methodA’s param :methodB
分析:
① 执行test1.methodB()当前线程会持有test1对象的锁
② 执行test2.methodB()当前线程会持有test2对象的锁
③ 当前线程持有了两把锁,但是锁的对象不一样,所以互相没有影响
例三:
public class Test {
public synchronized void methodA(String param) {
System.out.println("methodA's param :" + param);
}
public synchronized void methodB(){
methodA("methodB");
}
public static void main(String[] args) {
Test test = new Test();
ThreadTest thread1 = test.new ThreadTest("thread1");
ThreadTest thread2 = test.new ThreadTest("thread2");
thread1.start();
thread2.start();
}
class ThreadTest extends Thread {
public ThreadTest(String name){
super(name);
}
public void run() {
Test test = new Test();
test.methodB();
}
}
}
运行结果:
methodA’s param :methodB
methodA’s param :methodB
分析:
① thread1先获取test的锁
② thread1执行methodB
③ thread1执行methodA
④ thread1释放test的锁
⑤ thread2获得test的锁
⑥ thread2执行methodB
⑦ thread2执行methodA
⑧ thread2释放test的锁
总结:例三和例二结果一样,但是执行顺序却不同,另外synchronized还有很多种用法,在分析时一定要搞清楚哪个线程获得了哪个对象的锁,就很容易明白了。