要求:电影院三个窗口同是卖100张票
1. 按照正常线程问题的结果
package 出票;
//会出现重复100票数,0也会出现
//原因:在线程抢占执行权的时候,可能会出现1号抢到后执行,但是在tickets--还有没有执行
//结束后又再次输出100,出现的混乱
//出现0也是因为--在执行后被线程抢占出现错误数据
public class sell extends Thread{
//private 修饰私有的,不被外界所改变值
//因为三个窗口一起卖票,所以用static修饰
//表明是共有的
private static int tickets=100;
public void run() {
while(true) {
if(tickets>0) {
System.out.println(getName()+"正在出售"+(tickets--)+"张票");
}
}
}
}
package 出票;
public class demo {
public static void main(String[] args) {
sell s1=new sell();
sell s2=new sell();
sell s3=new sell();
//无参构造起窗口名字
s1.setName("1号");
s2.setName("2号");
s3.setName("3号");
//开始运行
s1.start();
s2.start();
s3.start();
}
}
2. 第一次改进
改进后依然是会有重复代码出现, 因为虽然加锁,但是锁对象是匿名出现的,所以没有体现一次进入一个程序的效果
package 出票__改进;
public class sellgai implements Runnable{
//成员变量
private static int tickets=100;
@Override
public void run() {
while(true) {
/**出现了0和负数
解决方案:就是将多条语句对共享数据操作的代码,用一个代码包起来---->代码--->同步代码块
格式:
synchronized(锁对象){
针对多条语句对共享数据操作代码 }
*/
//给代码加入锁,这样每一个线程进来后其他线程就不会进来
//但是测试后依然有错误数据,原因是匿名自定义了锁对象
//锁对象就相当于一把钥匙
//这样无法起到一个线程进来后其他线程进不来的效果
//因为每个线程都会匿名一个对象,所以同步代码块就没有意义
synchronized (new Object()) {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}
}
}
3.第二次改进
最终改进成功,不再出现重复数据和错误数据
package 出票__改进2;
public class sellgai2 implements Runnable{
//成员变量
private static int tickets=100;
//在成员变量建立私有的锁对象
//相当于只有一把钥匙,每个进程要进去都要先获得锁对象
//有锁对象的可以执行,没有的等待前一个执行结束后重新抢钥匙再执行
//这样就实现了一次进去一个进程
private Object obj=new Object();
@Override
public void run() {
while(true) {
synchronized (obj) {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}
}
}
4.lock锁
package lock锁;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class selllock implements Runnable {
private static int tickets=100;
private Lock lock=new ReentrantLock();
@Override
public void run() {
while(true) {
try {
lock.lock();
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
}else {
System.exit(0);
}
//要在进程结束后释放lock锁,不然会出现如果一个线程抢到
//那么其他线程就不会在执行
//finally相当于把钥匙还回去,线程再重新抢
} finally {
if(lock!=null) {
lock.unlock();
}
}
}
}
}