最近遇到一个售票系统的题目,我用了3种方法逐渐完善,希望能对您有所帮助,如有不当敬请指出。
不加锁Runnable版
package com.qf.a_safe;
/**
* 1. 铁道部发布了一个售票任务,要求销售1000张票,要求有10个窗口来进行销售,请编写多线程程序来模拟这个效果
iv.窗口001正在销售第1000张票
v.窗口001正在销售第999张票
vi.窗口002正在销售第998张票
vii.。。。
viii.窗口010正在销售第1张票
ix.票已经销售完毕
*
*/
/**
* 不加锁Runnable版
*
*/
class Task2 implements Runnable{
private int ticket2 = 1000;//1000张票
@Override
public void run() {
//while (true){}可以避免"票已经销售完毕"打印次数少的状况
while (true) {
if (ticket2>0) {
System.out.println(Thread.currentThread().getName() +"正在销售第"+ticket2+"张票");
ticket2--;
} else {
System.out.println(Thread.currentThread().getName() +"票已经销售完毕");
break;
}
}
}
}
public class Test1_2 {
public static void main(String[] args) {
Task2 task2 = new Task2();
for (int i = 1; i <= 10; i++) {
if (i<=9) {
//这里需要注意,若不先new Task2()直接写new Thread(new task2,"窗口00"+i).start();
//会出现每个线程都卖1000张票的情况,因为对象不唯一会导致每个线程对象都有一份独立的成员属性;
new Thread(task2,"窗口00"+i).start();
} else {
new Thread(task2,"窗口010").start();
}
}
}
}
不加锁Thread版本
class MyTread2 extends Thread{
//用static修饰来保证ticket3的唯一性,Runnable中不用static修饰是因为Task2对象唯一
private static int ticket3 = 1000;
public MyTread2(String name) {
super(name);
}
@Override
public void run() {
while (true) {
if (ticket3>0) {
System.out.println(this.getName() +"正在销售第"+ticket3+"张票");
ticket3--;
} else {
System.out.println(this.getName() +"票已经销售完毕");
break;
}
}
}
}
public class Test_3 {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
if (i<=9) {
new MyTread2("窗口00"+i).start();
} else {
new MyTread2("窗口010").start();
}
}
}
}
锁对象Runnable版本
package com.qf.a2_safe;
class Task implements Runnable{
private int ticket = 1000;//因为对象只有一个
@Override
public void run() {
synchronized (this) {//因为对象只有一个
while (true) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"卖出"+ticket+"张票");
ticket--;
} else {
System.out.println(Thread.currentThread().getName()+"票已卖完");
break;
}
}
}
}
}
public class Suo {
public static void main(String[] args) {
Task task = new Task();
for (int i = 1; i <= 10; i++) {
if (i<=9) {
//这里需要注意,若不先new Task2()直接写new Thread(new task2,"窗口00"+i).start();
//会出现每个线程都卖1000张票的情况,因为对象不唯一会导致每个线程对象都有一份独立的成员属性;
new Thread(task,"窗口00"+i).start();
} else {
new Thread(task,"窗口010").start();
}
}
}
}
对象锁Thread版本
package com.qf.a_safe;
/**
* 1. 铁道部发布了一个售票任务,要求销售1000张票,要求有5个窗口来进行销售,请编写多线程程序来模拟这个效果
iv.窗口001正在销售第1000张票
v.窗口001正在销售第999张票
vi.窗口002正在销售第998张票
vii.。。。
viii.窗口05正在销售第1张票
ix.票已经销售完毕
目标:
方式1: 继承Thread,创建线程
方式2: 实现Runnable接口
通过案例分析: 继承Thread VS Runnable
使用方式1分析案例:
问题1: 每个窗口都卖了1000张票
原因: 每个线程对象都有一份独立的成员属性(1000张票)
处理: 变为静态属性
问题2: 有部分重票
原因: 多个线程抢占资源,一个线程打印票没进行--操作,另一个线程又打印了
处理: 加锁----如何加锁---
加锁方式有3种: 1. 同步代码块 2. 同步方法 3,对象互斥锁(容易出现死锁)
加锁的注意事项: 1.确保是同一把锁 2. 锁的范围
问题3: 出现负数的问题
原因: 判断出现问题,
处理: 在同步锁内部继续加判断
继续优化:有多少窗口卖票,就应该要有多少窗口退出
解决方案: while(true){} ,然后从一个出口退出
*
*/
/**
* 锁对Thread象版
*
*/
class MyThread12 extends Thread{
private static int ticket = 1000; //销售1000张票
private static Object obj = new Object();
public MyThread12(String name) {
super(name);
}
@Override
public void run() {
while(true){
//锁对象,要确保是同一把锁(同一个对象)
synchronized ("lock"/*obj*/) { //静态属性:obj,字符串常量
if(ticket>0){
System.out.println(super.getName()+"正在销售第"+ticket+"张票");
ticket--;
}else{
System.out.println(super.getName()+"票已经销售完毕");
break;
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
if (i<=9) {
new MyThread12("窗口00"+i).start();
} else {
new MyThread12("窗口010").start();
}
}
}
}