1. synchronized同步方法
多个线程调用synchronized声明的方法一定是排队执行的。而且只有共享资源的读写访问才需要同步化,如果不是共享资源,那根本没有同步的必要。
(1)脏读
多线程调用同一个实例对象的同一个方法时,为了避免数据不同步的出现,使用了synchronized关键。
虽然在赋值的方法上做了同步,但是再取值的方法上没有做同步,取值了的时候出现了意象不到的情况,这就是脏读(dirtyRead)。脏读的情况发生在读取变量的时候,此值已经被其他线程修改了。
(2)锁重入
关键字synchronized具有锁重入的功能。就是在使用synchronized的时候,当一个线程得到一个对象锁后,再次请求该对象锁时是可以得到该多想的锁的。这也说明在一个synchronized方法/块内调用本类的其他synchronized方法/块时,是永远可以得到锁的。
“可重入锁”的概念是:自己可以再次获得自己的内部锁。当一个线程获得了某个对象的锁之后,此时这个锁对象还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的。如果不可以锁重入的话,就会造成死锁。
可重入锁也支持在父子继承的环境中:
public class Main {
public int i = 5;
synchronized public void mainPrint() {
i --;
System.out.println("i=" + i);
}
}
public class ThreadTest extends Main{
synchronized public void testPrint() {
while(i > 0) {
i --;
System.out.println("i="+i);
}
}
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
new ThreadTest().testPrint();
}
};
thread.start();
}
}
输出结果:
i=4
i=3
i=2
i=1
i=0
此例子说明,当存在子父类继承关系时,子类完全可以通过“可重入锁”调用父类的同步方法。(注意是同一个锁才可以)
(3)同步方法或代码块出现异常时,所持有的锁自动释放。
(4)同步不具有继承性,就是说父类的同步方法如果被子类重写,如果子类重写的时候非同步,那么通过子类类型实例调用该方法时,不同步。
2. 同步代码块
使用同步方法有时候是有弊端的。比如一个线程调用同步方法执行一个比较耗时的任务,那么其他线程必须等待较长时间。这样的情况可以使用同步代码块来解决。
两个线程访问同一个对象中的synchronized(this)同步代码块时,一段时间内只能被一个线程执行,另个线程只能等到当前线程执行完这段代码后才能继续执行。
多个线程调用同一个对象的不同名称的synchronized方法或者synchronized(this)代码块的时候,调用效果就是按顺序执行的。因为this代表该对象,而同步方法的锁也是该对象。
(1)静态同步synchronized方法与synchronized(class)代码块。
关键字synchronized可以加在静态方法,如果这样写,是对当前.java文件对应的Class类进行持锁。Class锁可以对类的所有对象实例起作用。