Java多线程

入门

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

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

并发:

​ 同一时刻,有多个指令再单个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)]

你可能感兴趣的:(java,jvm,开发语言)