多线程学习
This commit is contained in:
parent
9afc59fc65
commit
dfa5a3299c
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.markilue.java_learning.thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: java_learning
|
||||||
|
* @BelongsPackage: com.markilue.java_learning.thread
|
||||||
|
* @Author: dingjiawen
|
||||||
|
* @CreateTime: 2022-09-16 17:27
|
||||||
|
* @Description:
|
||||||
|
* TODO 死锁问题:
|
||||||
|
* 不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。
|
||||||
|
* 一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class DeadLock {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Object g = new Object();
|
||||||
|
Object m = new Object();
|
||||||
|
//两个人彼此都需要对方的同步锁释放才能运行下面的同步锁内容,但是对方都没有释放锁的操作
|
||||||
|
Owner s = new Owner(g,m);
|
||||||
|
Customer c = new Customer(g,m);
|
||||||
|
new Thread(s).start();
|
||||||
|
new Thread(c).start();
|
||||||
|
|
||||||
|
/*
|
||||||
|
先给钱
|
||||||
|
先发货
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Owner implements Runnable{
|
||||||
|
private Object goods;
|
||||||
|
private Object money;
|
||||||
|
|
||||||
|
public Owner(Object goods, Object money) {
|
||||||
|
super();
|
||||||
|
this.goods = goods;
|
||||||
|
this.money = money;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (goods) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println("先给钱");
|
||||||
|
synchronized (money) {
|
||||||
|
System.out.println("发货");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Customer implements Runnable{
|
||||||
|
private Object goods;
|
||||||
|
private Object money;
|
||||||
|
|
||||||
|
public Customer(Object goods, Object money) {
|
||||||
|
super();
|
||||||
|
this.goods = goods;
|
||||||
|
this.money = money;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (money) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println("先发货");
|
||||||
|
synchronized (goods) {
|
||||||
|
System.out.println("再给钱");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
package com.markilue.java_learning.thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: java_learning
|
||||||
|
* @BelongsPackage: com.markilue.java_learning.thread
|
||||||
|
* @Author: dingjiawen
|
||||||
|
* @CreateTime: 2022-09-16 16:05
|
||||||
|
* @Description:
|
||||||
|
* TODO 线程之间的通信:
|
||||||
|
* 当线程之间彼此额操作具有先后顺序,比如线程A做包子,线程B吃包子,需要在A做完以后B在做时,两个线程之间需要怎么进行通信呢? ->等待唤醒机制
|
||||||
|
* 等待唤醒机制:就是在一个线程进行了规定操作后,就进入等待状态wait(), 等待其他线程执行完他们的指定代码过后 再将其唤醒notify();
|
||||||
|
* 在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。wait/notify 就是线程间的一种协作机制。
|
||||||
|
* 1) wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
|
||||||
|
* 2) notify:则选取所通知对象的 wait set 中的一个线程释放;
|
||||||
|
* 3) notifyAll:则释放所通知对象的 wait set 上的全部线程。
|
||||||
|
* 需要注意的是:并不是notify以后就一定直接从wait的地方恢复运行,而是需要看是否存在同步锁的竞争问题,只有不存在或者其他线程的该同步锁代码运行完,才恢复
|
||||||
|
* 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE(可运行) 状态;
|
||||||
|
* 否则,线程就从 WAITING 状态又变成 BLOCKED(等待锁) 状态
|
||||||
|
* 还需要注意的是:
|
||||||
|
* 1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
|
||||||
|
* 2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
|
||||||
|
* 3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class ThreadCommunicate {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
//等待唤醒案例
|
||||||
|
BaoZi bz = new BaoZi();
|
||||||
|
|
||||||
|
//这里使用同一个bz,同时在同步锁中也是加入bz,以此保证注意1、2、3
|
||||||
|
ChiHuo ch = new ChiHuo("吃货",bz);
|
||||||
|
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
|
||||||
|
|
||||||
|
ch.start();
|
||||||
|
bzp.start();
|
||||||
|
|
||||||
|
/*
|
||||||
|
包子铺开始做包子
|
||||||
|
包子造好了:厚皮牛肉大葱
|
||||||
|
吃货来吃吧
|
||||||
|
吃货正在吃:厚皮,牛肉大葱包子
|
||||||
|
包子铺开始做包子
|
||||||
|
包子造好了:薄皮蟹黄灌汤
|
||||||
|
吃货来吃吧
|
||||||
|
吃货正在吃:薄皮,蟹黄灌汤包子
|
||||||
|
包子铺开始做包子
|
||||||
|
包子造好了:厚皮牛肉大葱
|
||||||
|
吃货来吃吧
|
||||||
|
吃货正在吃:厚皮,牛肉大葱包子
|
||||||
|
包子铺开始做包子
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class BaoZi {
|
||||||
|
String pier ;
|
||||||
|
String xianer ;
|
||||||
|
boolean flag = false ;//包子资源 是否准备好 包子资源状态
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ChiHuo extends Thread{
|
||||||
|
private BaoZi bz;
|
||||||
|
|
||||||
|
public ChiHuo(String name,BaoZi bz){
|
||||||
|
super(name);
|
||||||
|
this.bz = bz;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while(true){
|
||||||
|
synchronized (bz){
|
||||||
|
if(bz.flag == false){//没包子
|
||||||
|
try {
|
||||||
|
bz.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("吃货正在吃:"+bz.pier+","+bz.xianer+"包子");
|
||||||
|
bz.flag = false;
|
||||||
|
bz.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BaoZiPu extends Thread {
|
||||||
|
|
||||||
|
private BaoZi bz;
|
||||||
|
|
||||||
|
public BaoZiPu(String name,BaoZi bz){
|
||||||
|
super(name);
|
||||||
|
this.bz = bz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int count = 0;
|
||||||
|
//造包子
|
||||||
|
while(true){
|
||||||
|
//同步
|
||||||
|
synchronized (bz){
|
||||||
|
if(bz.flag == true){//包子资源 存在
|
||||||
|
try {
|
||||||
|
bz.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有包子 造包子
|
||||||
|
System.out.println("包子铺开始做包子");
|
||||||
|
if(count%2 == 0){
|
||||||
|
// 薄皮 蟹黄包
|
||||||
|
bz.pier = "薄皮";
|
||||||
|
bz.xianer = "蟹黄灌汤";
|
||||||
|
}else{
|
||||||
|
// 厚皮 牛肉大葱
|
||||||
|
bz.pier = "厚皮";
|
||||||
|
bz.xianer = "牛肉大葱";
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
|
||||||
|
bz.flag=true;
|
||||||
|
System.out.println("包子造好了:"+bz.pier+bz.xianer);
|
||||||
|
System.out.println("吃货来吃吧");
|
||||||
|
//唤醒等待线程 (吃货)
|
||||||
|
bz.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
package com.markilue.java_learning.thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: java_learning
|
||||||
|
* @BelongsPackage: com.markilue.java_learning.thread
|
||||||
|
* @Author: dingjiawen
|
||||||
|
* @CreateTime: 2022-09-16 16:05
|
||||||
|
* @Description:
|
||||||
|
* TODO 线程之间的通信:等待唤醒机制之多对多问题:生产者消费者问题
|
||||||
|
* 案例:有家餐馆的取餐口比较小,只能放10份快餐,厨师做完快餐放在取餐口的工作台上,服务员从这个工作台取出快餐给顾客。现在有多个厨师和多个服务员。
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class ThreadCommunicate1 {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Workbench bench = new Workbench();
|
||||||
|
Cook c1 = new Cook("大拿",bench);
|
||||||
|
Cook c2 = new Cook("吉祥",bench);
|
||||||
|
Waiter w1 = new Waiter("翠花",bench);
|
||||||
|
Waiter w2 = new Waiter("如意",bench);
|
||||||
|
|
||||||
|
c1.start();
|
||||||
|
c2.start();
|
||||||
|
w1.start();
|
||||||
|
w2.start();
|
||||||
|
/*
|
||||||
|
大拿厨师制作了一份快餐,现在工作台上有:1份快餐
|
||||||
|
大拿厨师制作了一份快餐,现在工作台上有:2份快餐
|
||||||
|
大拿厨师制作了一份快餐,现在工作台上有:3份快餐
|
||||||
|
大拿厨师制作了一份快餐,现在工作台上有:4份快餐
|
||||||
|
大拿厨师制作了一份快餐,现在工作台上有:5份快餐
|
||||||
|
如意服务员取走了一份快餐,现在工作台上有:4份快餐
|
||||||
|
如意服务员取走了一份快餐,现在工作台上有:3份快餐
|
||||||
|
翠花服务员取走了一份快餐,现在工作台上有:2份快餐
|
||||||
|
吉祥厨师制作了一份快餐,现在工作台上有:3份快餐
|
||||||
|
吉祥厨师制作了一份快餐,现在工作台上有:4份快餐
|
||||||
|
吉祥厨师制作了一份快餐,现在工作台上有:5份快餐
|
||||||
|
翠花服务员取走了一份快餐,现在工作台上有:4份快餐
|
||||||
|
翠花服务员取走了一份快餐,现在工作台上有:3份快餐
|
||||||
|
翠花服务员取走了一份快餐,现在工作台上有:2份快餐
|
||||||
|
翠花服务员取走了一份快餐,现在工作台上有:1份快餐`
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Workbench {
|
||||||
|
private static final int MAX_VALUE = 10;
|
||||||
|
private int num;
|
||||||
|
public synchronized void put() {
|
||||||
|
while(num >= MAX_VALUE){
|
||||||
|
try {
|
||||||
|
this.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
//加入睡眠时间是放大问题现象,去掉同步和wait等,可观察问题
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
System.out.println(Thread.currentThread().getName()+ "厨师制作了一份快餐,现在工作台上有:" + num + "份快餐");
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
public synchronized void take() {
|
||||||
|
while(num <=0){
|
||||||
|
try {
|
||||||
|
this.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
//加入睡眠时间是放大问题现象,去掉同步和wait等,可观察问题
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
num--;
|
||||||
|
System.out.println(Thread.currentThread().getName()+"服务员取走了一份快餐,现在工作台上有:" + num + "份快餐");
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Waiter extends Thread{
|
||||||
|
private Workbench workbench;
|
||||||
|
|
||||||
|
public Waiter(String name,Workbench workbench) {
|
||||||
|
super(name);
|
||||||
|
this.workbench = workbench;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
workbench.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Cook extends Thread{
|
||||||
|
private Workbench workbench;
|
||||||
|
|
||||||
|
public Cook(String name,Workbench workbench) {
|
||||||
|
super(name);
|
||||||
|
this.workbench = workbench;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
workbench.put();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ public class ThreadDemo1 {
|
||||||
//当main打印到5之后,需要等join进来的线程停止后才会继续了。
|
//当main打印到5之后,需要等join进来的线程停止后才会继续了。
|
||||||
if(i==5){
|
if(i==5){
|
||||||
try {
|
try {
|
||||||
|
//join以后,如果这个线程不停,程序就不会往下运行,所以start是开始运行,join以后是必须等他运行完再往后运行
|
||||||
t.join();
|
t.join();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.markilue.java_learning.thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: java_learning
|
||||||
|
* @BelongsPackage: com.markilue.java_learning.thread
|
||||||
|
* @Author: dingjiawen
|
||||||
|
* @CreateTime: 2022-09-16 14:42
|
||||||
|
* @Description:
|
||||||
|
* TODO 线程安全问题演示:
|
||||||
|
* 电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个
|
||||||
|
* (本场电影只能卖100张票)。
|
||||||
|
* 我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class ThreadSafe {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 创建线程任务对象
|
||||||
|
Ticket ticket = new Ticket();
|
||||||
|
// 创建三个窗口对象
|
||||||
|
Thread t1 = new Thread(ticket, "窗口1");
|
||||||
|
Thread t2 = new Thread(ticket, "窗口2");
|
||||||
|
Thread t3 = new Thread(ticket, "窗口3");
|
||||||
|
// 同时卖票
|
||||||
|
t1.start();
|
||||||
|
t2.start();
|
||||||
|
t3.start();
|
||||||
|
/*
|
||||||
|
线程安全问题在于:多个线程在操作同一个共享变量时,可能出现多重篡改的问题
|
||||||
|
窗口1正在卖:5
|
||||||
|
窗口3正在卖:4
|
||||||
|
窗口2正在卖:5
|
||||||
|
窗口2正在卖:3
|
||||||
|
窗口3正在卖:1
|
||||||
|
窗口1正在卖:2
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//第一步 创建资源类,在资源类中定义属性和方法
|
||||||
|
public static class Ticket implements Runnable {
|
||||||
|
private int ticket = 100;
|
||||||
|
|
||||||
|
//第二步,在资源操作方法中进行判断和干活
|
||||||
|
private void saleTicket(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (ticket > 0) { // 有票可以卖
|
||||||
|
// 出票操作
|
||||||
|
// 使用sleep模拟一下出票时间
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// 获取当前线程对象的名字
|
||||||
|
String name = Thread.currentThread().getName();
|
||||||
|
System.out.println(name + "正在卖:" + ticket--);
|
||||||
|
}else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 执行卖票操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 每个窗口卖票的操作
|
||||||
|
// 窗口永远开启
|
||||||
|
while (true) {
|
||||||
|
saleTicket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package com.markilue.java_learning.thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: java_learning
|
||||||
|
* @BelongsPackage: com.markilue.java_learning.thread
|
||||||
|
* @Author: dingjiawen
|
||||||
|
* @CreateTime: 2022-09-16 14:52
|
||||||
|
* @Description: TODO 线程安全问题演示:
|
||||||
|
* 第二种情况演示 :使用一个static变量ticket来作为共享变量,放入常量池中,因此new ticket操作的都是同一个ticket变量
|
||||||
|
* <p>
|
||||||
|
* TODO 解决线程安全问题的方法:同步代码块或者同步方法或者锁机制(暂时不管)
|
||||||
|
* 1)同步代码块:
|
||||||
|
* synchronized (需要锁住的东西如 ThreadSafe1.class){需要锁的代码
|
||||||
|
* }
|
||||||
|
* 2)同步方法:
|
||||||
|
* 在方法名前加synchronized关键字
|
||||||
|
* 如public synchronized void method(){
|
||||||
|
* 可能会产生线程安全问题的代码
|
||||||
|
* }
|
||||||
|
* 同步方法的锁对象:
|
||||||
|
* (1)静态方法:当前类的Class对象
|
||||||
|
* (2)非静态方法:this
|
||||||
|
* 所以当需要创建多个对象时,需要锁住的方法必须使用static关键字
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class ThreadSafe1 {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
Ticket t1 = new Ticket("窗口一");
|
||||||
|
Ticket t2 = new Ticket("窗口二");
|
||||||
|
Ticket t3 = new Ticket("窗口三");
|
||||||
|
|
||||||
|
t1.start();
|
||||||
|
t2.start();
|
||||||
|
t3.start();
|
||||||
|
/*
|
||||||
|
窗口一正在卖:10
|
||||||
|
窗口二正在卖:10
|
||||||
|
窗口三正在卖:9
|
||||||
|
窗口一正在卖:7
|
||||||
|
窗口二正在卖:8
|
||||||
|
窗口三正在卖:5
|
||||||
|
窗口一正在卖:4
|
||||||
|
窗口二正在卖:6
|
||||||
|
窗口二正在卖:3
|
||||||
|
窗口三正在卖:3
|
||||||
|
窗口一正在卖:3
|
||||||
|
窗口三正在卖:1
|
||||||
|
窗口二正在卖:0
|
||||||
|
窗口一正在卖:2
|
||||||
|
|
||||||
|
加了同步代码块以后,连顺序都是对的:
|
||||||
|
窗口二正在卖:47
|
||||||
|
窗口二正在卖:46
|
||||||
|
窗口二正在卖:45
|
||||||
|
窗口三正在卖:44
|
||||||
|
窗口三正在卖:43
|
||||||
|
窗口三正在卖:42
|
||||||
|
窗口一正在卖:41
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Ticket extends Thread {
|
||||||
|
private static int ticket = 100;
|
||||||
|
|
||||||
|
public Ticket() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ticket(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 执行卖票操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 每个窗口卖票的操作
|
||||||
|
// 窗口永远开启
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
synchronized (Ticket.class) { //这里不能选用this作为锁,因为这几个线程的this不是同一个,如果是同一个ticket对象才可以使用
|
||||||
|
|
||||||
|
if (ticket > 0) { // 有票可以卖
|
||||||
|
// 出票操作
|
||||||
|
// 使用sleep模拟一下出票时间
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// 获取当前线程对象的名字
|
||||||
|
System.out.println(getName() + "正在卖:" + ticket--);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue