1.线程安全问起模拟
2.lock锁解决线程安全问题
lock锁所在的类:java.util.concurrent.locks.ReentrantLock , 该类是:***java.util.concurrent.locks.Lock***接口的实现类用于创建所对象,Lock接口中对于synchronized代码块的描述:
*Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。*
两者的区别Lock中这样描述:
synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取
和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被
获取时相同的词法范围内释放所有锁。
虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到
锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。例如,某些遍历并发访问的数据结果的算法要求
使用 "hand-over-hand" 或 "chain locking":获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并
获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许
以任何顺序获取和释放多个锁,从而支持使用这种技术
lock锁解决问题的代码:
public class SaveTicketImpl implements Runnable {
private int ticket = 100;
/**
* 在这里使用lock锁解决线程安全问题:
* 相比于synchronized块或者方法将锁对象的获取和释放都在代码中展示了出来
*
* 使用步骤
* 1.在方法外新建lock的实现类对象
* 2.在发生线程安全问题的代码之前调用lock方法
* 3.在发生线程安全问题的代码之后调用unlock方法
*/
//1.新建ReentrantLock对象
Lock l = new ReentrantLock();
@Override
public void run() {
//2.调用lock方法
l.lock();
try {
while(ticket>0){
Thread.sleep(20);
System.out.println(Thread.currentThread().getName()+"卖出第-->"+ticket+"张票");
ticket--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
//在结束后释放对象锁
l.unlock();
}
}
}
测试案例:
public class DemoSaveThreadSafe {
public static void main(String[] args) {
//使用同步代码块解决线程安全问题
SaveTicketImpl ticket1 = new SaveTicketImpl();
Thread tt1 = new Thread(ticket1);
Thread tt2 = new Thread(ticket1);
Thread tt3 = new Thread(ticket1);
tt1.start();
tt2.start();
tt3.start();
}
}
测试结果:
Thread-0卖出第-->100张票
Thread-0卖出第-->99张票
Thread-0卖出第-->98张票
Thread-0卖出第-->97张票
.
.
.
Thread-0卖出第-->3张票
Thread-0卖出第-->2张票
Thread-0卖出第-->1张票