Java多线程

本文介绍了Java中实现多线程的三种方式:Thread、Runnable和Callable,讲解了线程的并发与并行概念,并展示了同步机制如synchronized和Lock的使用。此外,还探讨了线程的死锁问题、等待唤醒机制、线程状态以及线程池的应用,通过实例展示了如何在实际编程中处理并发问题。
摘要由CSDN通过智能技术生成

入门

线程:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

有了多线程之后,我们可以让程序同时做多件事情,提高效率,只要你想让多个事情同时运行,就需要使用到多线程。

并发:

​ 同一时刻,有多个指令再单个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()+" 已执行");
    }
}

核心原理

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下次再次提交任务时,池子不会创建新的线程对象,直接服用已经有的线程即可
  3. 但是如果提交任务时,池子中没有空闲的线程,也无法创建新的线程,任务就会排队等待

自定义线程池

图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值