1:线程为什么要同步?
在多线程的情况下,容易造成数据的安全问题。比如取钱操作,涉及到金钱方面的,强烈建议使用线程同步。
线程同步主要有两种方式,一种是synchronized(同步监视器),一个是lock锁。
synchronized
synchronized关键字的同步方式:
(1)同步代码块(实例代码块)
obj就是同步监视器;代码含义:线程开始执行同步代码块之前,必须先获取同步监视器的锁定。 “加锁——修改——释放锁”
(2)同步方法(普通同步方法,静态同步方法)
有关键字synchronized修饰的就是同步方法。
- 对于普通方法的同步方法:无需显示指定同步监视器,同步方法的同步监视器就是this,也就是调用该方法的对象。
- 如果是静态方法:那默认监视器就是当前类。
同一个类的多个对象作为线程target,访问同一资源可以用当前类作为同步监视器
同一个对象作为多个线程target,访问同一资源可以用当前对象(this)作为同步监视器。
注意:synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造器,成员变量。
同步锁(Lock)
Lock、ReadWriteLock是jdk 1.5 提供的两个接口;ReentrantLock(可重入锁)是Lock接口的实现。ReentrantReadWriteLock是ReadWriteLock的实现。
下图是ReentrantLock和ReentrantReadWriteLock的类图:
在线程安全中常使用的是ReentrantLock(可重入锁)
通常使用代码格式:
使用Lock与使用同步方法有点类似。只是使用Lock时,显式使用Lock对象作为同步锁。 必须显式调用unlock()释放同步锁。
而使用同步方法时是隐式的使用当前对象作为同步监视器。
2:释放同步监视器的锁定的情况
(1)当前线程的同步代码块、同步方法执行结束,当前线程即释放同步监视器。
(2)当前线程的同步代码块、同步方法执行遇到break,return终止了该代码块、方法的执行。
(3)当前线程的同步代码块、同步方法执行遇到异常,导致该代码块、方法异常终止。
(4)当前线程的同步代码块、同步方法执行时碰到同步监视器的wait()方法,则当前线程暂停,并释放同步监视器。
3:线程不会释放同步监视器的情况:
(1)线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程并不会释放同步监听器,但是会让出当前cpu执行权限。
(2)程序执行同步代码块或同步方法时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。(这就是suspend方法容易造成死锁的原因,你这个线程都抱着同步监视器“挂起”了,那我其它需要这个同步监视器的线程该怎么执行?如果该线程一直不被唤醒,那不就是死锁了嘛......)
4:死锁
概念:当两个线程互相等待对方释放同步监视器时就会发生死锁。