4.1 synchronized的作用
关键字synchronized取得的锁是对象锁,而不是把一段代码或方法当作锁,哪个线程先执行带synchronized的方法,哪个线程就有该方法所属对象的锁LOCK,其他线程只能等待。 (但是可以调用非synchronized方法)4.2 synchronized的可重入性
可重入锁的概念: 自己可以再次获取自己的内部锁,比如说有一条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想获得这个对象的锁的时候还是可以获得。从互斥锁的设计上来说,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功,在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重入性。
此外,还有一下特性:
- 子类是完全可以通过“可重入锁”调用父类的同步方法
- 出现异常,锁自动释放
- 同步不具有继承性(子类继承父类的同步方法,不具有synchronized效果)
4.3 synchronized同步代码块
4.3.1 synchronized方法的缺陷
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。4.3.2 同步代码块的使用方法
synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
4.3.2 同步代码块的理解
- 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
- 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
4.4 静态同步synchronized方法和synchronized(class)代码块
synchronized还可以应用在static静态方法上如下:synchronized public static void prints()
{
//业务逻辑
}
如果这样写,synchronized是对当前的*.java文件对应的Class类进行持锁。而synchronized关键字加到非静态方法上是给对象上锁。
- 同一个Class类的不同对象不能同时访问synchronized静态方法
- 对象锁和class类锁不一样,属于两种锁
实验如下:
public class test extends Thread {
private String name;
test(String name){this.name = name;}
synchronized static public void printA()
{
System.out.println("enter.....in: " + currentThread().getName());
try {
sleep(5000);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("enter.....out: " + currentThread().getName());
}
@Override
public void run() {
printA();
}
public static void main(String[] args)
{
try {
test test0 = new test("A");
test0.start();
test test1 = new test("B");
test1.start();
}
catch (Exception e)
{
System.out.println("ending...");
e.printStackTrace();
}
}
}
实验结果如下:
enter.....in: Thread-0
enter.....out: Thread-0
enter.....in: Thread-1
enter.....out: Thread-1
同步代码块synchronized(class)的作用其实和synchronized静态方法作用一样。
4.5 synchronized总结
4.5.1 synchronized同步方法
- 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态.
- 同一时间只有一个线程可以执行synchronized同步方法中的代码
4.5.2 synchronized同步代码块
- 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态.
- 同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
4.5.3 总结
- 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
- 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
- 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
- 尽量避免使用String对象来控制同步(JVM具有String常量池缓存功能)