目录
- 一、什么是多线程
- 二、多线程的两个概念(并发和并行)
- 三、多线程的实现方式
- 3.1 继承Thread类的方式进行实现
- 3.2 实现Runnable接口的方式进行实现
- 3.3 利用Callable接口和Future接口方式实现
- 四、常见的成员方法
- 五、线程的生命周期
- 六、线程安全的问题
- 七、同步方法
- 八、lock锁
- 九、死锁
- 十、生产者和消费者(等待唤醒机制)
- 十一、等待唤醒机制(阻塞队列方式实现)
- 十二、线程的状态
- 十三、练习
- 13.1 练习1:
- 13.2 练习2:
- 13.3 练习3:
- 13.4 练习4:
- 13.5 练习5:
- 13.6 练习6:
- 13.7 练习7
- 十四、线程池
- 十五、线程池多大合适
一、什么是多线程
进程:是程序的基本执行实体
线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
二、多线程的两个概念(并发和并行)
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
三、多线程的实现方式
3.1 继承Thread类的方式进行实现
public class MyThread extends Thread{
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(this.getName()+"Hello World!");
}
}
}
public static void main(String[] args) {
/*
* 多线程的第一种启动方式:
* 1.自己定义一个类继承Thread
* 2.重写run方法
* 3.创建子类的对象,并启动线程*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1:");
t2.setName("线程2:");
//开启线程
t1.start();
t2.start();
}
运行结果:
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程1:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
线程2:Hello World!
3.2 实现Runnable接口的方式进行实现
package myThread.case2;
public class MyRun implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
//获取当前线程的对象
Thread t = Thread.currentThread();
System.out.println(t.getName() + "Hello JAVA!");
}
}
}
public static void main(String[] args) {
//创建MyRun对象
MyRun mr = new MyRun();
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName("线程1:");
t2.setName("线程2:");
//开启线程
t1.start();
t2.start();
}
3.3 利用Callable接口和Future接口方式实现
特点:可以获取到多线程运行的结果
- 创建一个MyCallable实现Callable接口
- 重写call(是有返回值的,表示多线程运行的结果)
- 创建MyCallable的对象(表示多线程要执行的任务)
- 创建FutureTask的对象(作用管理多线程运行的结果)
- 创建Thread类的对象,并启动(表示线程)
package myThread.case3;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//求1~100的和
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread t1 = new Thread(ft);
t1.start();
Integer result = ft.get();
System.out.println(result);
}
四、常见的成员方法
String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置名字)
细节:
1、如果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X序号,从0开始的)
2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
static Thread currentThread() 获取当前线程的对象
细节:
当JVM虚拟机启动之后,会自动的启动多条线程
其中有一条线程就叫做main线程
他的作用就是去调用main方法,并执行里面的代码
在以前,我们写的所有的代码,其实都是运行在main线程当中
static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
细节:
1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
2、方法的参数:就表示睡眠的时间,单位毫秒
1 秒= 1000毫秒
3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
package myThread.ThreadMethod1;
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(super.getName()+"@"+i);
}
}
}
package myThread.ThreadMethod1;
public class ThreadMethod1 {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread("飞机");
MyThread t2 = new MyThread("坦克");
t1.start();
t2.start();
/*Thread t = Thread.currentThread();
System.out.println(t.getName());
System.out.println("1111111111111111");
Thread.sleep(5000);
System.out.println("222222222");*/
}
}
public static void main(String[] args) {
//创建线程要执行的参数对象
MyRunable mr = new MyRunable();
//创建线程对象
Thread t1 = new Thread(mr, "飞机");
Thread t2 = new Thread(mr, "坦克");
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
守护线程:
public static void main(String[] args) {
/*守护线程:
* 细节:
* 当其他的非守护线程执行完毕之后,守护线程会陆续结束*/
ThreadMethod1 t1 = new ThreadMethod1();
ThreadMethod2 t2 = new ThreadMethod2();
t1.setName("女神");
t2.setName("备胎");
//把第二个线程设置为守护线程(备胎线程)
t2.setDaemon(true);
t1.start();
t2.start();
}
礼让线程:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "@" + i);
//出让线程
Thread.yield();
}
}
}
public static void main(String[] args) {
/*礼让线程*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("飞机");
t2.setName("坦克");
t1.start();
t2.start();
}
public static void main(String[] args) throws InterruptedException {
/*插入线程/插队线程*/
MyThread t1 = new MyThread();
t1.setName("土豆");
t1.start();
//表示把t这个线程,插入到当前线程之前
t1.join();
//执行在main线程中的
for (int i = 0; i < 100; i++) {
System.out.println("main线程" + i);
}
}
五、线程的生命周期
六、线程安全的问题
练习:
需求:
某电影院目前正在上映国产大片,共有100张票,而它有3个窗口买票,请设计一个程序模拟 该电影院买票
public class MyThread extends Thread {
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;
@Override
public void run() {
while (true) {
if (ticket<100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(getName()+"正在卖第"+ticket+"张票!");
}else {
break;
}
}
}
}
public static void main(String[] args) {
/*某电影院目前正在上映国产大片,共有100张票,而它有3个窗口买票,请设计一个程序模拟 该电影院买票*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
public class MyThread extends Thread {
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;
//锁对象,一定要是唯一的
static Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代码块
synchronized (obj) {
if (ticket < 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(getName() + "正在卖第" + ticket + "张票!");
} else {
break;
}
}
}
}
}
七、同步方法
public class MyRunable implements Runnable {
int ticket = 0;
@Override
public void run() {
while (true) {
if (method()) break;
}
}
private synchronized boolean method() {
if (ticket == 100) {
return true;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票!!!");
}
return false;
}
}
八、lock锁
public class MyThread extends Thread {
static int ticket = 0;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// synchronized (MyThread.class){
lock.lock();
try {
if (ticket == 100) {
break;
} else {
Thread.sleep(100);
ticket++;
System.out.println(getName() + "正在卖第" + ticket + "张票!!!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
// }
}
}
}
九、死锁
十、生产者和消费者(等待唤醒机制)
public class Cook extends Thread {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断桌子上是否有食物
if (Desk.foodFlag == 1) {
//如果有,就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//如果没有,就制作食物
System.out.println("厨师做了一碗面条!!!");
//修改桌子上食物的状态
Desk.foodFlag = 1;
//叫醒等待的消费者开吃
Desk.lock.notifyAll();
}
}
}
}
}
}
public class Desk {
/*作用:控制消费者和生产者的执行*/
//是否有食物 0 :没有食物 1 :有食物
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
public class Foodie extends Thread {
/*
* 1. 循环
* 2. 同步代码块
* 3. 判断共享数据是否到了末尾(到了末尾)
* 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
* */
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断桌子上是否有食物
if (Desk.foodFlag == 0) {
//如果没有,就等待
try {
Desk.lock.wait();//让当前线程跟锁绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//把吃的总数-1
Desk.count--;
//如果有,就开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做,
Desk.lock.notifyAll();
//修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
十一、等待唤醒机制(阻塞队列方式实现)
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
queue.put("面条");
System.out.println("厨师放了一碗面条");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
/*
*
* 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
* 细节:
* 生产者和消费者必须使用同一个阻塞队列
*
* */
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
c.start();
f.start();
}
十二、线程的状态
十三、练习
13.1 练习1:
一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,
要求:请用多线程模拟卖票过程并打印剩余电影票的数量
public class MyThread extends Thread {
static int ticket = 1000;
@Override
public void run() {
synchronized (MyThread.class) {
while (true) {
if (ticket == 0) {
break;
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(getName()+"在买票,还剩下"+ticket+"张票!!!");
}
}
}
}
}
13.2 练习2:
有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出,
利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来.
public class MyRunable implements Runnable {
int count = 100;
@Override
public void run() {
//0.循环
while (true) {
//1.同步代码块
synchronized (MyRunable.class) {
//2.判断共享数据-已经到末尾
if (count < 10) {
System.out.println("礼物还剩下" + count + "份,不再送出!!!");
break;
} else {
//3.判断共享数据-没有到末尾
count--;
System.out.println(Thread.currentThread().getName()+"在赠送礼物,还剩下"+count+"份!!!");
}
}
}
}
}
public static void main(String[] args) {
/*有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出,
利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来.*/
//0.创建线程对象
MyRunable mr = new MyRunable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//1.给线程设置名称
t1.setName("1");
t2.setName("2");
//2.开启线程
t1.start();
t2.start();
}
13.3 练习3:
同时开启两个线程,共同获取1-100之间的所有数字。
要求:将输出所有的奇数。
public class MyRunable implements Runnable {
int num = 1;
@Override
public void run() {
//0.循环
while (true) {
//1.同步代码块
synchronized (MyRunable.class) {
//2.判断共享数据-已经到末尾
if (num > 100) {
break;
} else {
//3.判断共享数据-没有到末尾
if (num % 2 == 1) {
System.out.println(Thread.currentThread().getName() + "打印数字" + num);
}
num++;
}
}
}
}
}
13.4 练习4:
微信中的抢红包也用到了多线程。
假设:100块,分成了3个包,现在有5个人去抢。
其中,红包是共享数据。
5个人是5条线程。
打印结果如下:
XXX抢到了XXX元
XXX抢到了XXX元
XXX抢到了XXX元
XXX没抢到
XXX没抢到
public class MyThread extends Thread {
//共享数据
static double money = 100; // 红包总金额
static int count = 3; // 红包总个数
static final double MIN = 0.01;
@Override
public void run() {
//1.同步代码块
synchronized (MyThread.class) {
if (count == 0) {
//2.判断共享数据是否到末尾——已经到末尾
System.out.println(getName() + "没有抢到红包!!!");
} else {
//3.判断共享数据是否到末尾--没有到末尾
//随机获取抢到红包的金额
double prize = 0;
Random r = new Random();
if (count == 1) {
//直接就是最后剩余的钱
prize = money;
} else {
//范围0.1 0.1 99.8
double bounds = money - (count - 1) * MIN;
prize = r.nextDouble(bounds);
//如果prize随机到比最小值还小的数,那么需要强制转为0.1
if (prize == MIN) {
prize = MIN;
}
}
money -= prize;
count--;
System.out.println(getName() + "抢到了" + prize + "元的红包");
}
}
}
}
public static void main(String[] args) {
//创建5个线程的对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t5 = new MyThread();
//设置名字
t1.setName("小A");
t2.setName("小Q");
t3.setName("小丹丹");
t4.setName("小诗诗");
t5.setName("小慧慧");
//开启线程
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
保留精确小数的方法:
public class MyThread extends Thread {
//共享数据
//红包总金额
static BigDecimal money = BigDecimal.valueOf(100.0);
//红包个数
static int count = 3;
//最小抽奖金额
static final BigDecimal MIN = BigDecimal.valueOf(0.01);
@Override
public void run() {
//同步代码块
synchronized (MyThread.class) {
//判断-共享数据是否到末尾-已经到末尾
if (count == 0) {
System.out.println(getName() + "没有抢到红包!!!");
} else {
//判断-共享数据是否到末尾-没有到末尾
//中奖金额
BigDecimal prize;
//最后一次
if (count == 1) {
prize = money;
} else {
//获取抽奖范围
//money - (count-1)*MIN
double bounds = money.subtract(BigDecimal.valueOf(count - 1).multiply(MIN)).doubleValue();
Random r = new Random();
prize = BigDecimal.valueOf(r.nextDouble(bounds));
}
//设置抽中红包,小数点保留两位,四舍五入
prize = prize.setScale(2, RoundingMode.HALF_EVEN);
money = money.subtract(prize);
count--;
System.out.println(getName() + "抽中了" + prize + "元!!!");
}
}
}
}
13.5 练习5:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {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 MyThread extends Thread {
ArrayList<Integer> list;
public MyThread(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
synchronized (MyThread.class) {
if (list.size() == 0) {
break;
} else {
Collections.shuffle(list);
int prize = list.remove(0);
System.out.println(getName()+"又产生了一个"+prize+"元的大奖!!!");
}
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
13.6 练习6:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {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 MyThread extends Thread {
ArrayList<Integer> list;
public MyThread(ArrayList<Integer> list) {
this.list = list;
}
//线程1:
static ArrayList<Integer> list1 = new ArrayList<>();
//线程2
static ArrayList<Integer> list2 = new ArrayList<>();
@Override
public void run() {
while (true) {
synchronized (MyThread.class) {
if (list.size() == 0) {
if ("奖池1".equals(getName())) {
System.out.println("奖池1:" + list1+",最高奖项为:"+Collections.max(list1)+"元,总金额为:"+list1.stream().mapToInt(Integer::intValue).sum());
} else {
System.out.println("奖池2:" + list2+",最高奖项为:"+Collections.max(list2)+"元,总金额为:"+list2.stream().mapToInt(Integer::intValue).sum());
}
break;
} else {
Collections.shuffle(list);
int prize = list.remove(0);
if ("奖池1".equals(getName())) {
list1.add(prize);
} else {
list2.add(prize);
}
}
}
}
}
}
public class MyThread extends Thread {
ArrayList<Integer> list;
public MyThread(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run() {
ArrayList<Integer> boxList = new ArrayList<>();
while (true) {
synchronized (MyThread.class) {
if (list.size() == 0) {
System.out.println(getName() + ": " + boxList);
break;
} else {
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
}
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
13.7 练习7
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {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元
核心逻辑:获取线程抽奖的最大值(看成是线程运行的结果)
以上打印效果只是数据模拟,实际代码运行的效果会有差异
public class MyCallable implements Callable<Integer> {
ArrayList<Integer> list;
public MyCallable(ArrayList<Integer> list) {
this.list = list;
}
@Override
public Integer call() throws Exception {
ArrayList<Integer> boxList = new ArrayList<>();
while (true) {
synchronized (MyCallable.class) {
if (list.size() == 0) {
System.out.println(Thread.currentThread().getName() + "----" + boxList);
break;
} else {
Collections.shuffle(list);
int prize = list.remove(0);
boxList.add(prize);
}
}
Thread.sleep(10);
}
//把集合中的最大值返回
if (boxList.size() == 0) {
return null;
} else {
return Collections.max(boxList);
}
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {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元
核心逻辑:获取线程抽奖的最大值(看成是线程运行的结果)
以上打印效果只是数据模拟,实际代码运行的效果会有差异*/
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
MyCallable mc = new MyCallable(list);
FutureTask<Integer> ft1 = new FutureTask<>(mc);
FutureTask<Integer> ft2 = new FutureTask<>(mc);
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
int max1 = ft1.get();
int max2 = ft2.get();
System.out.println(max1);
System.out.println(max2);
if (max1>max2){
System.out.println("抽奖箱1产生了最大奖项,是"+max1+"元");
}else {
System.out.println("抽奖箱2产生了最大奖项,是"+max2+"元");
}
}
十四、线程池
public static void main(String[] args) {
//获取线程池对象
ExecutorService pool = Executors.newCachedThreadPool();//没有上限的线程池
//提交任务
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
//销毁线程池
// pool.shutdown();
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.submit(new MyRunable());
pool.shutdown();
}
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数一:核心线程数量 不能小于0
参数二:最大线程数 不能小于0,最大数量 >= 核心线程数量
参数三:空闲线程最大存活时间 不能小于0
参数四:时间单位 用TimeUnit指定
参数五:任务队列 不能为null
参数六:创建线程工厂 不能为null
参数七:任务的拒绝策略 不能为null
public static void main(String[] args) {
/*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
参数一:核心线程数量 不能小于0
参数二:最大线程数 不能小于0,最大数量 >= 核心线程数量
参数三:空闲线程最大存活时间 不能小于0
参数四:时间单位 用TimeUnit指定
参数五:任务队列 不能为null
参数六:创建线程工厂 不能为null
参数七:任务的拒绝策略 不能为null*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数量
6,//最大线程数
60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new LinkedBlockingQueue<>(),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
);
}
十五、线程池多大合适
public static void main(String[] args) {
//向Java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();
System.out.println(count);
}