需求 :某电影院目前正在上映国产大片,共有100张票,而他有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路 :
private int ticketCount = 100;public class Ticket implements Runnable {
private int ticketCount = 100;
@Override
public void run() {
while(true){
if (ticketCount<=0){
break;
}else{
this.ticketCount -= 1;
System.out.println(Thread.currentThread().getName()+"正在卖票,还剩有"+this.ticketCount+"张");
}
}
}
}public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket,"一号窗口");
Thread t2 = new Thread(ticket,"二号窗口");
Thread t3 = new Thread(ticket,"三号窗口");
t1.start();
t2.start();
t3.start();
}
}看似这个案例没有什么问题,但是在实际生活中,售票时候出票是需要一定的时间的,所以在出售一张票的时候需要一点时间的延迟,接下来就修改卖票程序中的动作,每次出票时间为100毫秒,用sleep()方法实现。
此时出现了问题
为什么出现这个问题(这也是我们判断多线程程序是否会有数据安全问题的标准)
如果解决多线程安全问题?
实现方法
锁多条语句操作共享数据,可以使用同步代码块实现
synchronized(任意对象){ 多条语句操作共享数据的代码 }
同步的好处和弊端
下面我们更新一下Ticket类。
public class Ticket implements Runnable {
private int ticketCount = 100;
private Object o = new Object();
@Override
public void run() {
while(true){
//锁对象,任意对象就可以.
//多个线程必须使用同一把锁
synchronized (o){
if (ticketCount==0){
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.ticketCount -= 1;
System.out.println(Thread.currentThread().getName()+"正在卖票,还剩有"+this.ticketCount+"张");
}
}
}
}
}
此时的运行结果变得非常自然。
同步方法:就是把synchronized关键字加到方法上。
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步静态方法:就是把synchronized关键词加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
主要记住:
虽然我们可以理解同步代码块和同步方法的锁对象,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更加清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
Lock是接口不能直接实例化,可以采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
下面我们使用Lock来更新一下Ticket类
public class Ticket implements Runnable {
private int ticketCount = 100;
//锁对象Lock
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//开启Lock锁
lock.lock();
try {
if (ticketCount == 0) {
break;
} else {
Thread.sleep(100);
this.ticketCount -= 1;
System.out.println(Thread.currentThread().getName() + "正在卖票,还剩有" + this.ticketCount + "张");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放Lock锁
lock.unlock();
}
}
}
}线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
建议:不要写锁的嵌套。