众所周知,synchronized关键字是Java语言中一种重要的同步机制,它既可以修饰方法,也可以修饰代码块。synchronized使用了对象的内部锁,使得每个线程在访问临界区时要先获得锁,保证了线程的安全。在JDK的早期版本中,synchronized的性能远远低于重入锁,但从JDK 6.0开始对synchronized进行了大量的优化,使得二者的性能差距不大。因此,在并发量并不是很高的情况下,使用synchronized完全可以满足要求。
相信读者对synchronized的用法都有一定了解,因此,这里说一些关于synchronized的细节:
1.对象锁和类锁
当synchronized修饰一个实例方法时,该方法的锁为this,即对象锁。
当synchronized修饰一个静态方法时,该方法的锁为该类的Class对象,即类锁。
可以根据实际的业务需求,选择锁的级别。
2. synchronized的锁重入性
synchronized关键字拥有锁重入的功能,也就是在使用synchronized时,当一个线程获得了一个对象的锁后,再次请求次对象时可以再次获得对象锁。示例如下:
public class ReentrantTest {
public static void main(String[] args) {
Thread th = new Thread(new MyTask());
th.start();
}
}
class MyTask implements Runnable{
public synchronized void run() {
System.out.println("MyTask start...");
//已获得对象锁的线程,可再次请求锁
fun1();
}
public synchronized void fun1(){
System.out.println("invoke fun1...");
fun2();
}
public synchronized void fun2(){
System.out.println("invoke fun2...");
}
}
运行结果:
MyTask start...
invoke fun1...
invoke fun2...
3.继承关系下的同步性
在创建了一个子类对象时,该对象内部维护了一个父类的对象。根据synchronized的锁重入性,子类的同步方法可以再次调用父类的同步方法,保证线程安全问题。示例如下:
public class SynExtends {
//父类
public static class Super{
public int count = 10;
//父类的同步方法
public synchronized void SupOperation(){
System.out.println("Super: count=" + count--);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//子类
public static class Sub extends Super{
//子类的同步方法
public synchronized void SubOperation(){
while (count > 0){
System.out.println("Sub: count=" + count--);
//调用父类的同步方法
super.SupOperation();
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
Sub sub = new Sub();
sub.SubOperation();
}
}).start();
}
}
运行结果:
Sub: count=10
Super: count=9
Sub: count=8
Super: count=7
Sub: count=6
Super: count=5
Sub: count=4
Super: count=3
Sub: count=2
Super: count=1
由此可见,可以通过同时在父类和子类上使用synchronized 来保证继承关系下的线程安全。