- 铁道部发布了一个售票任务,要求销售1000张票,要求有5个窗口来进行销售,请编写多线程程序来模拟这个效果
iv. 窗口001正在销售第1000张票
v. 窗口001正在销售第999张票
vi.窗口002正在销售第998张票
vii. 。。。
viii.窗口05正在销售第1张票
ix.票已经销售完毕
看到这个题目我们自然会用多线程的方式来实现:下面会从两种实现多线程的方式来分析
1.使用任务来实现多线程
问题1 出现部分重票
原因:每个窗口都要可能打印1000张
解决:加—锁的范围:锁打印和–
—锁的对象,同一个对象
锁的分类:1.同步代码块 2.同步方法 3.对象互斥锁
问题2.会出现负数
原因:多个线程都进入的while判断,在锁外等待
解决:在锁内继续判断-----if(ticket>0)
问题3.每个线程卖完票应该都有退出
解决:改完while(true),在判断中退出-----else{ bresk; }
代码部分使用1.同步代码块的方式实现,另外两种方式也已经写好并且注释了
2.同步方法:要注意使用返回值,然后把while循环放在同步方法外,利用每次返回的值来判断是否要弄卖完票了
3.对象互斥锁:要注意避免死锁,利用try{…}finally{…}来实现最后执行的关闭锁
class Task implements Runnable{
private int ticket = 1000;
private Object obj = new Object(); //成员属性
private Lock lock = new ReentrantLock(); //实例化互斥对象锁
@Override
public void run() {
save1();
//方式2:同步方法
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!this.save2()){//返回false则退出
break;
}
}
*/
//方式3:互斥对象锁 ---容易出现死锁 alt+shift+m:封装方法快捷键
//1.同一把锁--同一个lock 2.锁的范围
/*while(true){
try{
lock.lock();
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket);
ticket--;
}else {
System.out.println(Thread.currentThread().getName()+"票已经卖完");
break;
}
}finally{
lock.unlock();
}
}*/
}
//方式2:同步方法
public synchronized boolean save2() {
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket);
ticket--;
return true;
}else {
System.out.println(Thread.currentThread().getName()+"票已经卖完");
return false;
}
}
//方式1:同步代码块
public void save1() {
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//睡眠1毫秒 复现问题和复现抢占资源效果
synchronized (this) {//"lock"字符串常量在常量池只有一份或者用obj同一个对象
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket);
ticket--;
}else {
System.out.println(Thread.currentThread().getName()+"票已经卖完");
break;
}
}
}
}
}
public class TaskTest {
public static void main(String[] args) {
Task task = new Task();
for(int i=1;i<=5;i++){
new Thread(task,"窗口00"+i).start();
}
}
}
2.使用继承来实现多线程:
能否直接使用上面任务形式实现多线程的方式呢?-----不能,会出现以下问题
问题1:会重复卖5000张票
原因:有5个成员属性ticket–每个对象都有一份独立的成员属性
解决变为static
问题2:会有部分重票
原因:this不是一个对象了
解决:锁对象为字符串常量或者静态对象
class MyThread extends Thread{
private static int ticket=1000;
private static Lock lock = new ReentrantLock();
private static Object obj = new Object();
public MyThread(String name) {
super(name);
}
@Override
public void run() {
//save1();
/*
while(true){
if (!save2()) {
break;
}
}
*/
//3.互斥对象锁
while(true){
try {
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "票");
ticket--;
} else {
System.out.println(Thread.currentThread().getName() + "已经卖完");
break;
}
} finally {
lock.unlock();
}
}
}
//2.同步方法
private static synchronized boolean save2() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "票");
ticket--;
return true;
} else {
System.out.println(Thread.currentThread().getName() + "已经卖完");
return false;
}
}
//1.同步方法
private void save1() {
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "票");
ticket--;
} else {
System.out.println(Thread.currentThread().getName() + "已经卖完");
break;
}
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for(int i=1;i<=5;i++){
new MyThread("窗口00"+i).start();;
}
}
}