练习一:售票
需求:
一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为30毫秒,
请用多线程模拟卖票过程并打印剩余电影票的数量。
package com.gch.d13_my_thread_test.test1;
public class MyThread extends Thread{
/**
* 调用父类的有参构造器
* @param name:线程名
*/
public MyThread(String name){
super(name);
}
// 第一种方式实现多线程,测试类中MyThread会创建多次,所以需要加static
public static int ticket = 0; // 0 ~ 999
@Override
public void run() {
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyThread.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(ticket == 1000){
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName() + "卖出了第" + ticket + "张电影票,剩余"
+ (1000 - ticket) + "张电影票!");
}
}
}
}
}
package com.gch.d13_my_thread_test.test1;
/*
### 练习一:售票
需求:
一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,
请用多线程模拟卖票过程并打印剩余电影票的数量
*/
public class ThreadDemo {
public static void main(String[] args) throws Exception {
// 1.创建线程对象
Thread t1 = new MyThread("窗口1");
Thread t2 = new MyThread("窗口2");
// 2.开启线程
t1.start();
t2.start();
}
}
练习二:赠送礼物
需求:
有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出。
利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来。
package com.gch.d13_my_thread_test.test2;
public class MyThread extends Thread{
/**
* 调用父类的有参构造器
* @param name:线程名
*/
public MyThread(String name) {
super(name);
}
// 由于测试类MyThread将会被创建多次,所以加static,表示本类的所有对象都共享该变量
public static int gifts = 100;
@Override
public void run() {
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyThread.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(gifts == 9){
System.out.println("只剩" + gifts + "份礼品了,不再送出!");
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
gifts--;
System.out.println(getName() + "正在发送第" + (100 - gifts) + "份礼品,还剩余" + gifts + "份礼品!");
}
}
}
}
}
package com.gch.d13_my_thread_test.test2;
/*
### 练习二:赠送礼物
需求:
有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出。
利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来.
*/
public class ThreadDemo {
public static void main(String[] args) {
// 1.创建线程对象
Thread t1 = new MyThread("1");
Thread t2 = new MyThread("2");
// 2.开启线程
t1.start();
t2.start();
}
}
练习三:打印数字
需求:
同时开启两个线程,共同获取1-100之间的所有数字。
将输出所有的奇数。
package com.gch.d13_my_thread_test.test3;
/**
线程任务类
*/
public class MyRunnable implements Runnable {
// 第二种方式实现多线程,由于测试类中MyRunnable只创建一次,所以不需要加static
public int number = 1;
@Override
public void run() {
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyRunnable.class){
// 3.判断共享数据是否已经到达末尾
if(number > 100){
break;
}else{
// 4.判断共享数据是否已经到达末尾(如果没有到,执行核心逻辑)
if(number % 2 == 1){
System.out.println(Thread.currentThread().getName() + "打印输出:" + number);
}
number++;
}
}
}
}
}
package com.gch.d13_my_thread_test.test3;
/*
### 练习三:打印数字
需求:
同时开启两个线程,共同获取1-100之间的所有数字。
将输出所有的奇数。
*/
public class ThreadDemo {
public static void main(String[] args) {
// 1.创建线程任务类对象
Runnable target = new MyRunnable();
// 2.创建线程对象
Thread t1 = new Thread(target,"线程1");
Thread t2 = new Thread(target,"线程2");
// 3.开启线程
t1.start();
t2.start();
}
}
练习四:抽奖箱
需求:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
每次抽出一个奖项就打印一个(随机):
抽奖箱1 又产生了一个 10 元大奖
抽奖箱1 又产生了一个 100 元大奖
抽奖箱1 又产生了一个 200 元大奖
抽奖箱1 又产生了一个 800 元大奖
抽奖箱2 又产生了一个 700 元大奖
package com.gch.d13_my_thread_test.test5;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyThread extends Thread {
// 共享数据
// {10,5,20,50,100,200,500,800,2,80,300,700}
// 集合
List<Integer> list;
/**
* 有参构造器
* @param name:线程名
* @param list:奖池集合
*/
public MyThread(String name, List<Integer> list) {
super(name);
this.list = list;
}
@Override
public void run() {
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyThread.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(list.size() == 0){
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
// 继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
// Integer i = list.get(0);
// list.remove(0);
System.out.println(getName() + "又产生了一个" + prize + "元的大奖!");
}
}
// 把线程睡眠写在锁的外面,因为写在里面其他线程也进不来啊
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.gch.d13_my_thread_test.test5;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
需求:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
每次抽出一个奖项就打印一个(随机):
抽奖箱1 又产生了一个 10 元大奖
抽奖箱1 又产生了一个 100 元大奖
抽奖箱1 又产生了一个 200 元大奖
抽奖箱1 又产生了一个 800 元大奖
抽奖箱2 又产生了一个 700 元大奖
*/
public class ThreadTest {
public static void main(String[] args) {
// 1.创建奖池
List<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
System.out.println(list.size()); // 12
// 2.创建线程对象
Thread t1 = new MyThread("抽奖箱1",list);
Thread t2 = new MyThread("抽奖箱2",list);
// 3.启动线程
t1.start();
t2.start();
}
}
练习五:
需求: 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700}; 创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2” 随机从抽奖池中获取奖项元素并打印在控制台上,格式如下: 每次抽的过程中,不打印,抽完时一次性打印(随机),在此次抽奖过程中,抽奖箱1总共产生了6个奖项。 分别为:10,20,100,500,2,300最高奖项为300元,总金额为932元 在此次抽奖过程中,抽奖箱2总共产生了6个奖项。 分别为:5,50,200,800,80,700最高奖项为800元,总金额为1835元
package com.gch.d13_my_thread_test.test6;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyThread extends Thread {
// 共享数据
// {10,5,20,50,100,200,500,800,2,80,300,700}
// 集合
List<Integer> list;
/**
* 有参构造器
* @param name:线程名
* @param list:奖池集合
*/
public MyThread(String name, List<Integer> list) {
super(name);
this.list = list;
}
// 线程1:抽奖箱1
public static List<Integer> list1 = new ArrayList<>();
// 线程2:抽奖箱2
public static List<Integer> list2 = new ArrayList<>();
@Override
public void run() {
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyThread.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(list.size() == 0){
// 表示抽奖结束
// 遍历求和
int sum1 = 0; // 用于记录抽奖箱1的总金额
for (int i = 0; i < list1.size(); i++) {
sum1 += list1.get(i);
}
int sum2 = 0; // 用于记录抽奖箱2的总金额
for (int i = 0; i < list2.size(); i++) {
sum2 += list2.get(i);
}
if("抽奖箱1".equals(getName())){
System.out.println("抽奖箱1:" + list1 + " 最高奖项为:" + Collections.max(list1) + "元,总金额为:" + sum1 + "元!");
}else{
System.out.println("抽奖箱2:" + list2 + " 最高奖项为:" + Collections.max(list2) + "元,总金额为:" + sum2 + "元!");
}
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
// 继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
if("抽奖箱1".equals(getName())){
list1.add(prize);
}else{
list2.add(prize);
}
}
}
// 把线程睡眠写在锁的外面,因为写在里面其他线程也进不来啊
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.gch.d13_my_thread_test.test6;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*需求:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
每次抽的过程中,不打印,抽完时一次性打印(随机) 在此次抽奖过程中,抽奖箱1总共产生了6个奖项。
分别为:10,20,100,500,2,300最高奖项为300元,总金额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项。
分别为:5,50,200,800,80,700最高奖项为800元,总金额为1835元
*/
public class ThreadTest {
public static void main(String[] args) {
// 1.创建奖池
List<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
System.out.println(list.size()); // 12
// 2.创建线程对象
Thread t1 = new MyThread("抽奖箱1",list);
Thread t2 = new MyThread("抽奖箱2",list);
// 3.启动线程
t1.start();
t2.start();
}
}
....练习5代码改进:线程栈
线程栈:
- 在Java内存图当中,堆内存它是唯一的,而栈内存它不是唯一的,它是跟线程是有关系的,简单理解就是每一个线程都有自己的栈,所以说,我们一般把它称为线程栈。
- 在没有学多线程之前,所画的栈内存图都是main线程的栈,而程序的主入口main方法就是运行在main线程这个栈当中的。
- 每一个线程都有自己独立的栈空间!
线程栈内存图:
package com.gch.d13_my_thread_test.test6_;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyThread extends Thread {
// 共享数据
// {10,5,20,50,100,200,500,800,2,80,300,700}
// 集合
List<Integer> list;
/**
* 有参构造器
* @param name:线程名
* @param list:奖池集合
*/
public MyThread(String name, List<Integer> list) {
super(name);
this.list = list;
}
@Override
public void run() {
// 创建每个线程抽中奖项的集合
// 线程栈
List<Integer> boxList = new ArrayList<>();
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyThread.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(list.size() == 0){
// 表示抽奖结束
int sum = 0; // 记录每个线程抽中奖项的综合
for (int i = 0; i < boxList.size(); i++) {
sum += boxList.get(i);
}
System.out.println(getName() + ":" + boxList + " , 最高奖项为:" + Collections.max(boxList)
+ "元,总金额为:" + sum + "元!");
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
// 继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
}
}
// 把线程睡眠写在锁的外面,因为写在里面其他线程也进不来啊
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.gch.d13_my_thread_test.test6_;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*需求:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
每次抽的过程中,不打印,抽完时一次性打印(随机) 在此次抽奖过程中,抽奖箱1总共产生了6个奖项。
分别为:10,20,100,500,2,300最高奖项为300元,总金额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项。
分别为:5,50,200,800,80,700最高奖项为800元,总金额为1835元
*/
public class ThreadTest {
public static void main(String[] args) {
// 1.创建奖池
List<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
System.out.println(list.size()); // 12
// 2.创建线程对象
Thread t1 = new MyThread("抽奖箱1",list);
Thread t2 = new MyThread("抽奖箱2",list);
Thread t3 = new MyThread("抽奖箱3",list);
Thread t4 = new MyThread("抽奖箱4",list);
// 3.启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
package com.gch.d13_my_thread_test.test7;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
// 共享数据
// {10,5,20,50,100,200,500,800,2,80,300,700}
// 集合
List<Integer> list;
/**
* 有参构造器
* @param list:奖池集合
*/
public MyCallable(List<Integer> list) {
this.list = list;
}
@Override
public Integer call() throws Exception {
// 创建每个线程抽中奖项的集合
// 线程栈
List<Integer> boxList = new ArrayList<>();
// 1.循环
while(true){
// 2.同步代码块
synchronized(MyCallable.class){
// 3.判断共享数据是否已经到了末尾(到了末尾)
if(list.size() == 0){
// 表示抽奖结束
int sum = 0; // 记录每个线程抽中奖项的综合
for (int i = 0; i < boxList.size(); i++) {
sum += boxList.get(i);
}
System.out.println(Thread.currentThread().getName() + ":" + boxList + " , 最高奖项为:" + Collections.max(boxList)
+ "元,总金额为:" + sum + "元!");
break;
}else{
// 4.判断共享数据是否已经到了末尾(没有到末尾,执行核心逻辑)
// 继续抽奖
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
}
}
// 把线程睡眠写在锁的外面,因为写在里面其他线程也进不来啊
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
// 把集合的最大值返回
if(boxList.size() == 0){
// 表示当前线程一个奖都没抽到
return null;
}else{
return Collections.max(boxList);
}
}
}
package com.gch.d13_my_thread_test.test7;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadTest {
public static void main(String[] args) throws Exception {
/*
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为 "抽奖箱1", "抽奖箱2"
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300
最高奖项为300元,总计额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700
最高奖项为800元,总计额为1835元
在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元
核心逻辑:获取线程抽奖的最大值(看成是线程运行的结果)
说白了就是获取两条线程的最大值
以上打印效果只是数据模拟,实际代码运行的效果会有差异
*/
// 1.创建奖池
List<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
System.out.println(list.size()); // 12
// 2.创建MyCallable任务对象,创建多线程要运行的参数对象
Callable<Integer> call = new MyCallable(list);
// 3.把Callable任务对象交给FutureTask对象作为线程任务对象
// 创建多线程要运行的管理者对象
// 线程一
FutureTask<Integer> ft1 = new FutureTask<>(call);
// 线程二
FutureTask<Integer> ft2 = new FutureTask<>(call);
// FutureTask<Integer> ft1 = new FutureTask<>(new MyCallable(list));
// FutureTask<Integer> ft2 = new FutureTask<>(new MyCallable(list));
// 4.创建线程对象
Thread t1 = new Thread(ft1,"抽奖箱1");
Thread t2 = new Thread(ft2,"抽奖箱2");
// 5.启动线程
t1.start();
t2.start();
// Integer max1 = ft1.get();
// Integer max2 = ft2.get();
System.out.println("抽奖池1最高奖项:" + ft1.get());
System.out.println("抽奖池2最高奖项:" + ft2.get());
}
}