十八、多线程JUC

news2024/11/18 22:43:57

目录

  • 一、什么是多线程
  • 二、多线程的两个概念(并发和并行)
  • 三、多线程的实现方式
    • 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接口方式实现

特点:可以获取到多线程运行的结果

  1. 创建一个MyCallable实现Callable接口
  2. 重写call(是有返回值的,表示多线程运行的结果)

  1. 创建MyCallable的对象(表示多线程要执行的任务)
  2. 创建FutureTask的对象(作用管理多线程运行的结果)
  3. 创建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);
    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1521483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos安装与集群搭建

Nacos安装与集群搭建 Nacos安装指南1.Windows安装1.1.下载安装包1.2.解压1.3.端口配置1.4.启动1.5.访问 2.Linux安装2.1.安装JDK2.2.上传安装包2.3.解压2.4.端口配置2.5.启动 3.Nacos的依赖Nacos集群搭建1.集群结构图2.搭建集群2.1.初始化数据库2.2.配置Nacos2.3.启动2.4.nginx…

JavaScript进阶:js的一些学习笔记-this指向,call,apply,bind,防抖,节流

文章目录 1. this指向1. 箭头函数 this的指向 2. 改变this的指向1. call()2. apply()3. bind() 3. 防抖和节流1. 防抖2. 节流 1. this指向 1. 箭头函数 this的指向 箭头函数默认帮我们绑定外层this的值&#xff0c;所以在箭头函数中this的值和外层的this是一样的箭头函数中的…

Linux——线程池

线程池的概念 线程池也是一种池化技术&#xff0c;可以预先申请一批线程&#xff0c;当我们后续有任务的时候就可以直接用&#xff0c;这本质上是一种空间换时间的策略。 如果有任务来的时候再创建线程&#xff0c;那成本又要提高&#xff0c;又要初始化&#xff0c;又要创建数…

揭示数据在内存中存储的秘密!

** ** 悟已往之不谏&#xff0c;知来者犹可追 ** ** 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#xff0c;别忘了给个免费的赞哟~ 整数在内存中的存储 整数的表达方式有三种&#xff1a;原码、反码、补码。 三种表示方法均有符号位和数值位两部分…

【Java】List, Set, Queue, Map 区别?

目录 List, Set, Queue, Map 区别&#xff1f; Collection和Collections List ArrayList 和 Array区别&#xff1f; ArrayList与LinkedList区别? ArrayList 能添加null吗&#xff1f; ArrayList 插入和删除时间复杂度&#xff1f; LinkedList 插入和删除时间复杂度&…

51单片机基础篇系列-定时/计数器的控制工作方式

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” 定时/计数器的控制 80C51单片机定时/计数器的工作由两个特殊功能寄存器控制&#xff0c;TMOD用于设置其工作方式&#xff1a; 1.工作方式寄存器TMOD 工作方式寄存器TMO…

IO流(主要是记住四大类InputStream,OutputStream、Reader和Writer,其他都是他们的子类)

IO流 1、文件 &#xff08;1&#xff09;文件概念 文件就是保存数据的地方。例如word文档&#xff0c;txt文件&#xff0c;execl文件等等。 &#xff08;2&#xff09;文件流 文件在程序中是以流的形式来操作的。 流&#xff1a;数据在数据源&#xff08;文件&#xff09;…

【MySQL基础】MySQL基础操作三

文章目录 &#x1f349;1.联合查询&#x1f95d;笛卡尔积 &#x1f349;2.内连接&#x1f95d;查询单个数据&#x1f95d;查询多个数据 &#x1f349;3.外连接&#x1f349;4.自连接&#x1f349;5.合并查询 &#x1f349;1.联合查询 &#x1f95d;笛卡尔积 实际开发中往往数…

docker 、postgres 数据库常用命令大全,持续更新

使用postgres 数据库&#xff0c;经常会忘记命令。今天抽时间整理一份常用命令列表&#xff0c;附带实践 使用docker 执行命令,导出数据库 docker exec -i postgres-container pd_dump -U postgres -d zxapp > /opt/zxapp.sql或者进入交互式界面&#xff1a; docker exec -i…

算力,承载AI无限可能

年后不久&#xff0c;美国人工智能研究公司OpenAI推出的Sora模型引起了广泛关注&#xff0c;对于人工智能发展之快的感慨还未过去&#xff0c;3月初&#xff0c;Anthropic丢出一颗“王炸”Claude3&#xff0c;其中Claude 3 Opus宣称在基准测试中优于OpenAI的GPT-4和Google的Gem…

vue3模块化引用组件和引用ts,调用ts中的接口

以简单的登录功能为例子 1.在util中创建loginValidators.ts import { ref, reactive } from vueinterface User{email: string;password: string; }export const loginUserreactive<User>({email: ,password: })interface Rules{email: {required: boolean;message: …

【MySQL】3. 库的操作

库的操作 1. 创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...]create_specification:[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_name说明&#xff1a; 大写的表示关键字 …

时间日期格式化

在创建的项目中&#xff0c;往往涉及到时间日期的参数&#xff0c;都不是显示正常&#xff08;中国&#xff09;时区&#xff0c;至于是那儿的时区小编也不知道&#xff01;但是&#xff0c;我们可以自定义返回的时间日期格式&#xff01; public class DataUtils {/*** 2021-0…

强化学习------DDPG算法(附pytorch代码)

目录 一、前言二、基本原理2.1、经验回放2.2、更新过程2.2.1、Critic网络更新过程2.2.2、Actor网络更新过程2.2.3、 目标网络的更新 2.3、噪音探索 三、算法代码实现四、训练示例4.1、实现效果 一、前言 Deep Deterministic Policy Gradient (DDPG)算法是DeepMind团队提出的一…

Guitar Pro2024中文免费版吉他爱好者必备工具,学习演奏、绘谱创作全覆盖

Guitar Pro8是一款功能强大的吉他工具&#xff0c;它支持多种乐器&#xff0c;包括但不限于吉他、贝斯、钢琴和鼓。这意味着&#xff0c;无论是吉他手、贝斯手、钢琴家还是鼓手&#xff0c;都可以利用这款软件进行音乐创作和演奏。 在Guitar Pro8中&#xff0c;用户可以轻松选…

【论文阅读】Improved Denoising Diffusion Probabilistic Models

Improved Denoising Diffusion Probabilistic Models 文章目录 Improved Denoising Diffusion Probabilistic Models概述Improving the Log-likelihoodLearning ∑ θ ( x t , t ) \sum_{\theta}(x_{t}, t) ∑θ​(xt​,t)Improving the Noise ScheduleReducing Gradient Nois…

数字多空策略(实盘+回测+数据)

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

2023年总结:聊一聊我这10年做过的副业

以下是我的2023年终总结&#xff0c;我从公众号同步到CSDN&#xff0c;大家可以看看我这10年的副业经验&#xff0c;希望对大家有所帮助。 今天是2023年最后一天&#xff0c;今年是不平凡的一年&#xff0c;也是变动最大的一年&#xff0c;大家也知道&#xff0c;嘟嘟最近离职了…

深度学习模型部署(十)模型部署配套工具二

上篇blog讲了trtexec和onnx_graphsurgeon两个工具&#xff0c;一个用于将onnx转化为trt模型&#xff0c;另一个用于对onnx模型进行修改。这篇blog讲polygraphy和nsight systems&#xff0c;前者用于进行模型优化以及结果验证&#xff0c;后者用于性能分析。 polygraph polygra…

Python程序设计基础——代码习题

1 __name__属性 import demodef main():if __name__ __main__:print(这个程序被直接运行。)elif __name__demo:print(这个程序作为模块被使用。) main()3.3 编写程序&#xff0c;生成包含1000个0~100之间的随机整数&#xff0c;并统计每个元素出现的次数。 import randomx[r…