竞争条件:
Java多线程中多个线程对同一数据进行存取操作,可能出现某一线程尚未完成对该数据的操作,而另一线程又对该数据进行操作,造成了数据错误。
竞争条件产生的原因是对数据的操作不是原子操作。例如:两个线程同时执行count+=1;
这条语句被处理过程如下:
1)将count加载到寄存器;
2)增加1;
3)将结果写回count。
当线程1执行步骤1、2后,失去运行权,线程2被唤醒执行了上述步骤,然后线程1被唤醒,继续执行步骤3,那么前述操作擦掉了第二个线程所做的更新。
同时由此可见,多线程中执行的中断不是语句级别甚至不是代码可见级别的,而是机器指令级别的
为了避免竞争条件情况的发生,就需要能够确保线程在失去控制之前运行完成,Java提供了两种机制对线程加锁,防止代码块受并发访问的干扰:1)声明ReentrantLock对象;2)使用synchronized关键字
1)声明锁对象
锁对象
在run()方法外声明锁对象,在run()内通过锁对象加锁,run运行结束前释放锁。在线程run方法中
Lock l = new ReentrantLock();//声明并创建一个可以被用来保护临界区的可重入锁
Lock l2 = new ReentrantLock(boolean fair);//声明并创建一个带有公平策略的锁,会降低程序性能,不必要的时候不用
public void run(){
l.lock();//加锁
try{
……
}finaly{
l.unlock();//释放锁
}
}
此结构保证一个线程封锁锁对象后,其他线程无法通过lock语句,当其他线程调用lock时线程被阻塞,直到为对象加锁的线程释放锁对象
解锁操作放在finaly语句至关重要,如果在临界区的代码被抛出异常,锁必然被释放,否则其他线程将被永远阻塞
1、如果两个线程试图访问同一个线程对象,则锁一串行方式提供服务;如果两个线程访问不同的线程对象,每个线程对应不同的锁对象,则两个线程不会发生阻塞。
2、锁是可重入的,被一个锁保护的代码可以调用另一个使用相同的锁的方法
条件对象
某个线程已经获得了线程锁,但是该线程需要满足一定条件才能继续执行,但是该条件需要别的线程进行操作。使用条件对象来管理那些已经获得的了锁但是并不能继续工作的线程。
一个锁可以有一个或多个相关的条件对象,可以用锁对象的newCondition()方法获取一个条件对象。通常将一个条件对象命名为能反应他锁表达的条件的名字,如设置一个条件对象来表达售票程序中“余票充足”条件
常用方法:
Condition sufficientTicket;//声明条件对象
sufficientTicket=l.newCondition();//通过锁对象获取一个条件对象
sufficientTicket.await();
//当某个条件不能满足,需要等待其他线程来使得该条件满足的时候, 调用await方法,释放锁暂停执行
sufficitentTicket.singalAll();
//另一个线程使得该条件满足后,通知所有被该条件对象阻塞的线程,当线程获取锁后则可以在await语句后继续执行
条件的使用过程:
首先,在线程类中声明一个条件对象;然后在类的构造函数中为
2)使用synchronized关键字