入门
线程:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
有了多线程之后,我们可以让程序同时做多件事情,提高效率,只要你想让多个事情同时运行,就需要使用到多线程。
并发:
同一时刻,有多个指令再单个cpu上交替执行
并行:
同一时刻,有多个指令再多个cpu上同时执行
多线程的三种实现方式
Thread
package Thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadDemo {
/**
* 多线程的第一种使用方式
* 1. 自己定义一个类继承Thread类
* 2. 重写run方法
* 3. 创建子类的对象,并启动线程
*/
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("thread-1");
t2.setName("thread-2");
t1.start();
t2.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.sss");
for (int i = 0; i < 5; i++) {
System.out.println(getName()+":"+sdf.format(new Date()));
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
Runnable
package Thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RannableDemo {
/**
* 多线程的第二种实现方法
* 1. 自己定义一个实现Runnable接口
* 2. 重写里面的run方法
* 3. 创建自己的类对象
* 4. 创建一个Thread类的对象,并开启线程
*/
public static void main(String[] args) {
// 创建MyRun对象 表示多线程要执行的任务
MyRun run1 = new MyRun();
MyRun run2 = new MyRun();
// 创建线程对象
Thread t1 = new Thread(run1);
Thread t2 = new Thread(run2);
//线程设置线程名
t1.setName("thread-1");
t2.setName("thread-2");
// 开启线程
t1.start();
t2.start();
}
}
class MyRun implements Runnable{
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.sss");
//获取当前线程的对象
Thread thread = Thread.currentThread();
String threadName = thread.getName();
for (int i = 0; i < 5; i++) {
System.out.println(threadName+":"+sdf.format(new Date()));
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
Callable
package Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableDemo {
/**
* 多线程的第三种实现方式
* 特点:
* 可以获取多线程运行的结果
* 1. 创建一个类MyCallable实现Callable接口
* 2. 重写call(是有返回值的,表示多线程运行的结果)
* 3. 创建MyCallable的对象(表示多线程需要执行的任务)
* 4. 创建FutureTask的对象(作用是管理多线程运行的结果)
* 5. 创建Thread类的对象,并启动(表示线程)
*/
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 integer = ft.get();
System.out.println(integer);
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 100;
}
}
常用的成员方法
方法名称 | 说明 |
---|---|
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名称(构造方法也可以设置) |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定时间,单位毫秒 |
setPriority(int newPriority) | 设置线程优先级 |
final int getPriority() | 获取线程优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield() | 出让线程/礼让线程 |
public static void join() | 插入线程/插队线程 |
线程优先级
在Java中线程的优先级分为10档,最小为1,最大为10,默认是5
优先级越高只是代表抢到cpu的概率越高,并不代表就一定会先执行完
守护线程
当其他线程结束后,守护线程也会陆续结束,即使守护线程还没有执行完。
package Thread;
public class Demo {
public static void main(String[] args) {
Thread t1 = new Thread01();
Thread t2 = new Thread02();
t1.setName("非守护线程");
t2.setName("守护线程");
t2.setDaemon(true);
t1.start();
t2.start();
}
}
class Thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println(getName()+": "+i);
}
}
}
class Thread02 extends Thread{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(getName()+": "+i);
}
}
}
上述代码中的非守护线程执行完之后,非守护线程会陆续结束,就不会再执行到结束了
应用:qq聊天的同时传输文件,当聊天结束关闭聊天框就会导致文件传输结束
礼让线程
public class Demo {
public static void main(String[] args) {
Thread t1 = new Thread01();
Thread t2 = new Thread01();
t1.setName("线程-01");
t2.setName("线程-02");
t1.start();
t2.start();
}
}
class Thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println(getName()+": "+i);
Thread.yield();
}
}
}
礼让线程就是当前多线程执行到指定位置后将当前线程让出去,然后重新和其他线程一下再次抢夺cpu的使用权,当然当前的线程也是有可能重新抢到cpu的执行权的。
插队线程
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread01();
t1.setName("线程-01");
t1.start();
//表示把 t1 线程插入到当前线程之前
//当前线程 main
t1.join();
//执行在main线程当中
for (int i = 0; i < 10; i++) {
System.out.println("main线程 " + i);
}
}
}
class Thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println(getName()+": "+i);
Thread.yield();
}
}
}
锁的使用
同步代码块synchronized
package Thread;
/**
* @Author chen_jiapin
* @Date 2023/6/7
* @Version 1.0
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread01();
t1.setName("线程-01");
Thread t2 = new Thread01();
t2.setName("线程-02");
Thread t3 = new Thread01();
t3.setName("线程-03");
t1.start();
t2.start();
t3.start();
}
}
class Thread01 extends Thread{
static int count;
static int num =1 ;
static Object lock = new Object();
@Override
public void run() {
while (true){
//synchronized (lock){
synchronized (Thread01.class){
if (num<=1000){
// try {
// sleep(20);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
System.out.println(getName()+" 抢到了第 "+num+" 张票"+ "\t" + ++count);
num++;
}else {
break;
}
}
}
}
}
synchronized() 中文意思是同步,也称之为”同步锁“。
synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
同步方法
用法:
修饰符 synchronized 返回值类型 方法名(方法参数){…}
public synchronized int fun(){…}
特点:
同步方法是锁住方法里面所有的代码
锁对象不能自己指定 --> 非静态的 :this 静态的:当前类的字节码文件对象
public class ThreadDemo {
/**
* 多线程的第一种使用方式
* 1. 自己定义一个类继承Thread类
* 2. 重写run方法
* 3. 创建子类的对象,并启动线程
*/
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
int ticket = 0 ;
@Override
public void run() {
//循环
while(true){
// //同步代码块,(同步方法)
// synchronized (MyRunnable.class){
// //判断共享数据是否到末尾
// if (ticket==100){
// return;
// }else {
// try {
// Thread.sleep(30);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// ticket++;
// System.out.println(Thread.currentThread().getName()+" : "+ticket);
// }
// }
if (method()) break;
}
}
private synchronized boolean method(){
if (ticket==100){
return true;
}else {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName()+" : "+ticket);
}
return false;
}
}
Lock锁
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock() 获得锁
void unlock() 释放锁
Lock是接口不能直接实例化,这里采用他的实现类ReentrantLock来实现实例化ReentrantLockd的构造方法
ReentrantLock() 创建一个ReentrantLock的实例
public class ThreadDemo {
/**
* 多线程的第一种使用方式
* 1. 自己定义一个类继承Thread类
* 2. 重写run方法
* 3. 创建子类的对象,并启动线程
*/
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
int ticket = 0 ;
Lock lock = new ReentrantLock();
@Override
public void run() {
//循环
while(true){
lock.lock();//加锁
try{
//判断共享数据是否到末尾
if (ticket==100){
return;
}else {
Thread.sleep(30);
ticket++;
System.out.println(Thread.currentThread().getName()+" : "+ticket);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
}
}
死锁
public class ThreadDemo {
/**
* 多线程的第一种使用方式
* 1. 自己定义一个类继承Thread类
* 2. 重写run方法
* 3. 创建子类的对象,并启动线程
*/
public static void main(String[] args) {
Thread t1 = new DieLock();
Thread t2 = new DieLock();
t1.setName("线程A");
t2.setName("线程B");
t1.start();
t2.start();
}
}
class DieLock extends Thread{
static Object objA = new Object();
static Object objB = new Object();
@Override
public void run() {
if ("线程A".equals(getName())){
synchronized (objA){
System.out.println("线程A拿到objA锁,即将去拿objB锁。。。");
synchronized (objB){
System.out.println("线程B拿到objB锁");
}
}
}
if ("线程B".equals(getName())){
synchronized (objB){
System.out.println("线程B拿到objB锁,即将去拿objA锁。。。");
synchronized (objA){
System.out.println("线程B拿到objA锁");
}
}
}
}
}
等待唤醒机制
思路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kr20QAWR-1686554666044)(01-入门.assets/截屏2023-06-08 13.33.19.png)]
常用方法
方法名称 | 说明 |
---|---|
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll | 唤醒所有线程 |
代码实现
public class ThreadDemo {
/**
* 需求:
* 完成生成者和消费者(等待唤醒机制)的代码
* 实现线程轮流交替执行的效果
*/
public static void main(String[] args) {
Cook cook = new Cook();
Foodie foodie = new Foodie();
cook.setName("厨师");
foodie.setName("消费者");
cook.start();
foodie.start();
}
}
/**
* 生产者
*/
class Cook extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count==0){
break;
}
if (Desk.foodFlag==0){
System.out.println("厨师制作了一碗面条");
Desk.foodFlag=1;
Desk.lock.notifyAll();
}else {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
/**
* 消费者
*/
class Foodie extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count==0){
break;
}
if (Desk.foodFlag==1){
Desk.count--;
System.out.println("消费者吃了一碗面条,还有"+Desk.count+"碗!!!");
Desk.foodFlag=0;
Desk.lock.notifyAll();
}else {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
/**
* 控制生产者和消费者执行的 桌子
*/
class Desk {
//桌子上有没有面条 0 没有 1 有
public static int foodFlag = 0 ;
//总数 表示消费者的最大消费能力
public static int count = 10 ;
//锁对象
public static Object lock = new Object();
}
阻塞队列方式代码实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkKpOg7H-1686554666045)(01-入门.assets/截屏2023-06-08 14.16.42.png)]
public class ThreadDemo {
/**
* 需求:
* 利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
* 细节:
* 生产者和消费者必须使用同一个阻塞队列
*/
public static void main(String[] args) {
//创建阻塞队列的对象,队列最大值为1
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
producer.start();
consumer.setDaemon(true);
consumer.start();
}
}
class Producer extends Thread{
//生产者
ArrayBlockingQueue<String> queue;
static int count = 1 ;
public Producer(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
if (this.count==10){
break;
}
queue.put("面条"+this.count);
System.out.println("厨师制作了一碗面条"+this.count);
count++;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class Consumer extends Thread{
//消费者
ArrayBlockingQueue<String> queue;
public Consumer(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
String food = queue.take();
System.out.println("消费者获取到了"+food);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
线程的状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yo1luII2-1686554666045)(01-入门.assets/截屏2023-06-08 15.00.25.png)]
线程是没有运行的这一状态的,因为运行状态时,是jvm将线程丢给操作系统了,此时线程并不在jvm中,所以没有所谓的运行状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DTxSAzV4-1686554666045)(01-入门.assets/截屏2023-06-08 15.05.14.png)]
实战练习
1-1 卖电影票
一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,
要求: 请用多线程模拟卖票的过程,并打印剩余电影票数量
public class ThreadDemo {
public static void main(String[] args) {
SellingTickets t1 = new SellingTickets();
SellingTickets t2 = new SellingTickets();
t1.setName("窗口1 ");
t2.setName("窗口2 ");
t1.start();
t2.start();
}
}
class SellingTickets extends Thread{
static int ticket = 1000;
@Override
public void run() {
while (true){
synchronized (SellingTickets.class){
if (ticket==0){
break;
}
ticket--;
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName()+"售卖了一张电影票,剩余电影票为: "+ ticket);
}
}
}
}
1-2 送礼品
有100份礼品,两人同时发送,当剩下的礼品小雨10份的时候则不再送出
利用多线程模拟该过程并将线程的名字和礼物剩余的数量打印出来
public class ThreadDemo {
public static void main(String[] args) {
SendGift t1 = new SendGift();
SendGift t2 = new SendGift();
t1.setName("小A ");
t2.setName("小B ");
t1.start();
t2.start();
}
}
class SendGift extends Thread{
static int num = 100 ;
@Override
public void run() {
while (true){
synchronized (SendGift.class){
if (num<10){
break;
}
num--;
System.out.println(getName()+"送出了一份礼物,剩余礼物数量为: "+num);
}
}
}
}
1-3 打印奇数数字
同时开启两个线程,共同获取1-100之间的所有数字
要求:将输出所有的奇数
public class ThreadDemo {
public static void main(String[] args) {
PribtOddNumber t1 = new PribtOddNumber();
PribtOddNumber t2 = new PribtOddNumber();
t1.start();
t2.start();
}
}
class PribtOddNumber extends Thread{
static int num = 1 ;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try{
if (num>100){
break;
}
Thread.sleep(10);
if (num%2==1){
System.out.println(getName()+":"+num);
}
}catch (Exception e){
}finally {
num++;
lock.unlock();
}
}
}
}
1-4 抢红包
抢红包也用到了多线程
假设: 100块 ,分成3个红包,现在有5个人去抢
其中红包是共享数据
5个人是5条线程
打印结果如下 :
xxx抢到了xxx元
xxx抢到了xxx元
xxx抢到了xxx元
xxx没抢到
xxx没抢到
public class ThreadDemo {
public static void main(String[] args) {
RedPackage t1 = new RedPackage();
RedPackage t2 = new RedPackage();
RedPackage t3 = new RedPackage();
RedPackage t4 = new RedPackage();
RedPackage t5 = new RedPackage();
t1.setName("小明");
t2.setName("小华");
t3.setName("小红");
t4.setName("小绿");
t5.setName("小紫");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
class RedPackage extends Thread{
//红包金额
static int money = 100 ;
//剩余红包数
static int residue = 3 ;
//锁对象
static Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
if (money == 0 && residue==0){
System.out.println(getName()+"没抢到");
}else {
int thisMoney = money/residue;
money = money-thisMoney;
residue--;
System.out.println(getName()+ " 抢到了 "+thisMoney+" 元");
}
}catch (Exception e){
}finally {
lock.unlock();
}
}
}
1-5 抽奖箱抽奖
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为
{ 10 , 5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700 }
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2“
随机从抽奖池中获取奖项元素并打印在控制台上,格式如下
每次抽出一个奖项就打印一个(随机)
抽奖箱1 产生了一个 10 元大奖
抽奖箱1 产生了一个 100 元大奖
抽奖箱1 产生了一个 200 元大奖
抽奖箱1 产生了一个 800 元大奖
抽奖箱1 产生了一个 700 元大奖
…
public class ThreadDemo {
public static void main(String[] args) {
GetEncourage t1 = new GetEncourage();
GetEncourage t2 = new GetEncourage();
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
class GetEncourage extends Thread{
static int[] arr = {10 ,5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700};
static int count = 0 ;
static Lock lock = new ReentrantLock();
@Override
public void run() {
Random random = new Random();
while (true){
lock.lock();
try {
int index = random.nextInt(arr.length);
if (count==10){
break;
}
System.out.println(getName()+ " 产生了一个 " + arr[index] + "元大奖");
count++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}catch (Exception e){
}finally {
lock.unlock();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
好的写法
public class ThreadDemo {
public static void main(String[] args) {
//奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10 ,5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700);
GetEncourage t1 = new GetEncourage(list);
GetEncourage t2 = new GetEncourage(list);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
class GetEncourage extends Thread{
private List<Integer> list;
public GetEncourage(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true){
synchronized (GetEncourage.class){
if (list.size()==0){
break;
}
Collections.shuffle(list);
Integer remove = list.remove(0);
System.out.println(getName()+ " 产生了一个 " + remove + "元大奖");
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
1-6 多线程统计并求最大值
在上一题的基础上,每次抽取的过程不打印,抽完时一次性打印(随机)
在此次抽奖的过程中,抽奖箱1一共产生了6个奖项,
分别为:10,20,100,500,2,300,最高奖项为500,总金额为932
在此次抽奖的过程中,抽奖箱2一共产生了6个奖项,
分别为:5,50,200,800,80,700,最高奖项为800,总金额为1835
方法1
public class ThreadDemo {
public static void main(String[] args) {
//奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10 ,5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700);
GetEncourage t1 = new GetEncourage(list);
GetEncourage t2 = new GetEncourage(list);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
class GetEncourage extends Thread{
private List<Integer> list;
static List<Integer> list1 = new ArrayList<>();
static List<Integer> list2 = new ArrayList<>();
public GetEncourage(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true){
synchronized (GetEncourage.class){
if (list.size()==0){
if ("抽奖箱1".equals(getName())){
System.out.println("抽奖箱1:"+list1);
System.out.print("抽奖箱1");
printListInfo(list1);
}else {
System.out.println("抽奖箱2:"+list2);
System.out.print("抽奖箱2");
printListInfo(list2);
}
break;
}
Collections.shuffle(list);
Integer remove = list.remove(0);
// System.out.println(getName()+ " 产生了一个 " + remove + "元大奖");
if ("抽奖箱1".equals(getName())){
list1.add(remove);
}else {
list2.add(remove);
}
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void printListInfo(List<Integer> list){
final Integer[] max = {0};
final AtomicReference<Integer>[] sum = new AtomicReference[]{new AtomicReference<>(0)};
list.forEach(i->{
sum[0].updateAndGet(v -> v + i);
if (i> max[0]){
max[0] =i;
}
});
System.out.println("最大奖项为:" + max[0] + " , " + " 总金额为 " + sum[0]);
}
}
优化一下
public class ThreadDemo {
public static void main(String[] args) {
//奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10 ,5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700);
GetEncourage t1 = new GetEncourage(list);
GetEncourage t2 = new GetEncourage(list);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
}
}
class GetEncourage extends Thread{
private List<Integer> list;
public GetEncourage(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
ArrayList<Integer> boxList = new ArrayList<>();
while (true){
synchronized (GetEncourage.class){
if (list.size()==0){
AtomicReference<Integer> sum = new AtomicReference<>(0);
boxList.forEach(i->{
sum.set(sum.get() + i);
});
System.out.println(getName()+" : 最大值为 "+Collections.max(boxList)+",最大金额为 "+ sum);
break;
}
Collections.shuffle(list);
Integer remove = list.remove(0);
boxList.add(remove);
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
1-7 多线程之间的比较
在上一题的基础上继续完成以下操作,
在此次抽奖的过程中,抽奖箱1一共产生了6个奖项,
分别为:10,20,100,500,2,300,最高奖项为500,总金额为932
在此次抽奖的过程中,抽奖箱2一共产生了6个奖项,
分别为:5,50,200,800,80,700,最高奖项为800,总金额为1835
在此次抽奖过程中,抽奖箱2中产生了最大的奖项,该奖项金额为800
package Thread;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//奖池
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10 ,5 ,20 ,50 ,100 ,200 ,500 ,800 ,2 ,80 ,300 ,700);
CallEncourage callEncourage = new CallEncourage(list);
FutureTask<Integer> ft1 = new FutureTask<>(callEncourage);
FutureTask<Integer> ft2 = new FutureTask<>(callEncourage);
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
Integer integer1 = ft1.get();
Integer integer2 = ft2.get();
String str = integer1>integer2?"在此次抽奖过程中,抽奖箱1中产生了最大的奖项,该奖项金额为"+integer1:"在此次抽奖过程中,抽奖箱2中产生了最大的奖项,该奖项金额为"+integer2;
System.out.println(str);
}
}
class CallEncourage implements Callable<Integer>{
private List<Integer> list;
public CallEncourage(List<Integer> list) {
this.list = list;
}
@Override
public Integer call() throws Exception {
ArrayList<Integer> boxList = new ArrayList<>();
while (true) {
synchronized (GetEncourage.class) {
if (list.size() == 0) {
AtomicReference<Integer> sum = new AtomicReference<>(0);
boxList.forEach(i -> {
sum.set(sum.get() + i);
});
System.out.println(Thread.currentThread().getName() + " : 最大值为 " + Collections.max(boxList) + ",最大金额为 " + sum);
break;
}
Collections.shuffle(list);
Integer remove = list.remove(0);
boxList.add(remove);
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return Collections.max(boxList);
}
}
线程池
Executors: 线程池的工具类通过调用方法返回不同的线程池对象
方法名称 | 说明 |
---|---|
public static ExectorService newCachedThreadPool( ) | 创建一个没有上限的线程池 |
public static ExectorService newFixedThreadPool( int nThreads) | 创建有上限的线程池 |
线程池代码的实现
无上限
package Thread;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Author chen_jiapin
* @Date 2023/6/7
* @Version 1.0
*/
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1. 获取线程池对象
ExecutorService pool = Executors.newCachedThreadPool();
//2. 提交任务
pool.submit(new MyRunnable());
Thread.sleep(30);
pool.submit(new MyRunnable());
Thread.sleep(30);
pool.submit(new MyRunnable());
Thread.sleep(30);
pool.submit(new MyRunnable());
Thread.sleep(30);
pool.submit(new MyRunnable());
//3. 销毁线程池
pool.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 已执行");
}
}
有上限
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1. 获取线程池对象
// ExecutorService pool = Executors.newCachedThreadPool();
ExecutorService pool = Executors.newFixedThreadPool(3);
//2. 提交任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//3. 销毁线程池
pool.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 已执行");
}
}
核心原理
- 创建一个池子,池子中是空的
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下次再次提交任务时,池子不会创建新的线程对象,直接服用已经有的线程即可
- 但是如果提交任务时,池子中没有空闲的线程,也无法创建新的线程,任务就会排队等待
自定义线程池
图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UUPqL7NA-1686554666046)(01-入门.assets/截屏2023-06-12 14.09.12.png)]
线程池中的最大连接数的数量 = 核心线程数量 + 临时线程数量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNBJGqDo-1686554666046)(01-入门.assets/截屏2023-06-12 14.02.20.png)]
当线程过多时,首先是核心线程去处理任务,剩余的任务去排队等待,当排队等待的队伍满了之后才会去创建临时线程去处理剩下的任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gDEfi0Ak-1686554666046)(01-入门.assets/截屏2023-06-12 14.06.18.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-52YKtGQ7-1686554666046)(01-入门.assets/截屏2023-06-12 14.06.03.png)]
代码
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, //核心线程数量 不能小于0
6, //最大线程数量 不能小于0 且 >= 核心线程数量 = 核心线程数量 + 临时线程数量
60, //空闲线程最大存活时间 不能小于0
TimeUnit.SECONDS, //空闲线程最大存活时间单位,需要使用TimeUnit来指定
new ArrayBlockingQueue<>(3), //任务队列,指定队伍长度,不能为null
Executors.defaultThreadFactory(), //创建线程工厂
new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略
);
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" 已执行");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1EWzeSIK-1686554666046)(01-入门.assets/截屏2023-06-12 14.38.34-6551960.png)]