需求:在电影院买票案例
为了模拟更真实的场景,加入延迟操作(让我们线程睡100毫秒)
程序的设计是好的,但是结果有一些问题
1)同一张票被卖了多次
CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
2)出现了0或者负票
(延迟操作+线程的执行随机性)
通过刚才的这个程序,有安全问题(同票还是负票)
如何解决多线程的安全问题?
校验一个多线程程序是否有安全问题的隐患的前提条件:
1)当前程序是否是多线程环境
2)是否有共享数据
3)是否有多条语句对共享数据进行操作
看当前案例是否有多线程的安全问题:
1)是否是多线程环境 是
2)是否有共享数据 是
3)是否有多条语句对共享数据进行操作 是
现在就需要解决安全问题:
1)多线程环境 不能解决
2)对共享数据进行优化 不能解决
3)解决将多条语句对共享数据这一环进行解决
解决方案:就是将多条语句对共享数据操作的代码,用一个代码包起来---->代码--->同步代码块
格式:
synchronized(锁对象){
针对多条语句对共享数据操作代码;
}
锁对象:肯定一个对象,随便创建一个对象(匿名对象)
给刚才的这个程序加入了同步代码块,但是锁对象使用的匿名对象(每一个线程进来都有自己的锁),还是没有解决!
锁对象:每一个线程最总使用的锁对象,只能是同一把锁
举例:
private int ticket=100;
private Object obj =new Object();
@Override
public void run() {
while (true) {
// synchronized(new Object()) {
// if(ticket>0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket--+"张票");
// }
// }
synchronized(obj) {
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket--+"张票");
}
}
}
}
}
Lock锁和sychronized功能相似
举例:
public class SellTicket implements Runnable {
// 定义100张票
private int ticket = 100;
// 创建锁对象
private Lock l = new ReentrantLock();
public void run() {
while (true) {
try {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket-- + "张票");
}
} finally {
if (l != null) {
l.unlock();
}
}
}
}
}
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构
可以使用Lock锁进行具体的锁定操作类 提供了具体的实现类:ReentrantLock
加锁并且去释放锁
举例:
public class SellTicket implements Runnable {
// 定义100张票
private int ticket = 100;
// 创建锁对象
private Lock l = new ReentrantLock();
public void run() {
while (true) {
try {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket-- + "张票");
}
} finally {
if (l != null) {
l.unlock();
}
}
}
}
}
注释:最后要释放锁,调用时必须要用锁接口的多态。
死锁:两个或两个以上的线程,在执行的过程中出现互相等待的情况
解决死锁的办法生产消费设计模式:运用同步方法,等待唤醒机制,来进行解决。