当多个线程来访问同一个数据时非常容易出现线程安全问题。
同步:一个任务的完成需要依赖另一个任务时,只有等待被依赖的那个任务完成后,依赖的任务才能完成。要不都完成要不都失败。
举例:有两个a和b,都是向同一个账号取钱,假设账户中有800,a和b都是取800;因为线程时并发运行的,所以可能会发生在a取800但还没进行扣款b来取800了,最后a和b都完成了取钱,这显然是不正常的。
发生这个问题的原因是任务中存在两个操作,取钱和扣钱,在未同步的情况下没有一起完成就切换了线程。
同步代码块
因为run方法体内不同步安全性,为了解决这个问题,需要同步监视器来解决这个问题,使用的通用就是同步。
synchronized (obj) {
// 需要同步的内容
// 取钱
// 扣钱
// 要么一起完成,要么一起失败
}
同步代码块中使用共享资源资源作为同步监视器(就是上面那个obj),这样其实是在修改共享资源之前先加锁在修改完成之后释放锁。通过这种方式可以保证在任一时刻都只有一个线程操作该共享资源。
通常同步代码块存在于run方法体的开始到结束。
同步方法
使用synchronized关键字修饰某个方法时,这个方法成了同步方法。对于同步方法而言,不用显示的制定同步监视器,因为该方法存在的对象就是同步方法的同步监视器。
synchronized可以修饰方法、代码块,不能修饰类、构造器等
同步锁(Lock)
在jdk1.5,java提供另一种线程同步:它通过定义同步锁对象来实现同步。
Lock提供了比synchronized更广泛、灵活的操作方式。
在临界区之前使用lock.lock();临界区之后使用lock.unlock();
线程安全的类具有的特点
- 该类的对象可以被多个线程安全的访问;
- 每个调用对象的任意方法之后都将得到正确的结果;
- 每个调用对象的任意方法之后该对象保持合理状态。
释放同步监视器的情况:
- 当线程的同步代码块、同步方法执行结束;
- 执行了wait方法;