【Java EE初阶 --- 多线程(初阶)】多线程的实现案例

乐观学习,乐观生活,才能不断前进啊!!!

我的主页:optimistic_chen

我的专栏:c语言Java

欢迎大家访问~
创作不易,大佬们点赞鼓励下吧~

前言

之前博客对多线程的是什么和基本内容都有详细了解,目前对于多线程的运用还很浅显,不能发挥出多线程应有的实力。这篇博客将带来多线程的基本应用,它会用到什么地方?又会带来什么高效的运行效率?我们又会学到什么?诸位尽情期待…

单例模式

单例模式是设计模式之一
两个新名词,什么是设计模式?什么又是单例模式呢?

设计模式相当于棋谱是行业内大佬为新人撰写的一些高效便捷的思路和案例。棋手除了不断下棋提高实力外,就是尽可能多的研读棋谱,解决问题;而对于程序员来讲,设计模式就是棋手的棋谱,面对一些棘手的问题时,设计模式就是我们的制胜法宝,更加重要的是,设计模式可以大大减少程序员代码风格的不同(因为大家都是根据“棋谱”解决问题)

而单例模式就是众多“棋谱”中的一种,保证某个类在程序中只存在唯⼀⼀份实例(对象), ⽽不会创建出多个实例(只能new一个对象)

实现单例模式

单例模式的实现方式很多,最常见的就是”饿汉”和“懒汉“两种:主要区别就是创建对象的时机:创建对象早,就是饿汉模式(懂得都懂);反之,创建对象迟,就是懒汉模式。

这里一个关键是:构造方法设为 private(使单例模式生效)

饿汉模式·

在初始化(类加载)的时候就new好对象(提早)

class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {//获取实例
        return instance;//读操作
    }
}

实例提前创建过,就不涉及线程安全问题,只是一个单纯的读操作。

懒汉模式

使用时再 new 对象(能不创建就不创建)

class Singleton {
    private static volatile Singleton instance = null;
    Object locker=new Object();
    private Singleton() {
    }

    public static Singleton getInstance() {//获取实例
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {//当instance为空,创建实例
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在这里插入图片描述

阻塞队列

阻塞队列能是⼀种线程安全的数据结构,也遵守“先进先出”的基本原则,并且具有以下特性:
• 天然线程安全
• 当队列满的时候, 继续⼊队列就会阻塞, 直到有其他线程从队列中取⾛元素.
• 当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插⼊元素.

最主要的应用场合是 生产者消费者模型

生产者消费者模型

<举个例子> 过年包饺子,我负责擀饺子皮,妈妈和爸爸负责包饺子,持续一段时间后,整个工作结束。整个场景的资源是“饺子皮”,我是“生产者”,爸爸妈妈是“消费者”,这个场景就是“阻塞队列”

生产者消费者模型的优势:

  1. 解耦合
    在这里插入图片描述
  2. 削峰填谷
    在这里插入图片描述

生产者消费者模型要付出的代价:
1. 整体结果趋于复杂化,需要更多机器,生产环境难以管理
2. 影响效率

标准库中的阻塞队列

public interface BlockingQueue<E> extends Queue<E>{
}

BlockingQueue 是⼀个接⼝.所以不能直接去new, 其真正实现的类是 LinkedBlockingQueue.
在这里插入图片描述
本来,队列入队和出队操作是offer和poll,但是我们这里是多线程情况,得使用带有阻塞功能的put和take。

比如:当队列为空时,却被take时,就会出现阻塞
在这里插入图片描述
这是完美运行的生产者消费者模型:

 public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> queue=new LinkedBlockingQueue<>(1000);

        Thread producer=new Thread(()->{
            int n=0;
            while(true){
                try {
                    queue.put(n);
                    n++;
                    System.out.println("生产元素"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"producer");


        Thread consumer=new Thread(()->{
            while(true){
                try {
                    Integer n=queue.take();
                    System.out.println("消费元素"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"consumer");
        producer.start();
        consumer.start();
    }

在这里插入图片描述

模拟实现阻塞队列(生产者消费者模型)

import java.util.concurrent.BlockingQueue;


class MyBlockQueue{
    private String[] data=null;

    private int head;

    private int tail;

    private int size;

    public MyBlockQueue(int capacity){
        data=new String[capacity];
    }

    public void put(String elem) throws InterruptedException {
        synchronized (this){
            while(size>=data.length){
                //队列满了,阻塞
                this.wait();//此时阻塞,当队列不满时,唤醒
            }
            data[tail]=elem;
            tail++;
            if(tail>=data.length){
                tail=0;
            }
            size++;
            this.notify();
        }
    }
    public String take() throws InterruptedException {
        synchronized (this){
            while(size==0){//while二次判定队列是否为空
                //队列为空,阻塞
                this.wait();//队列不空时,唤醒
            }
            String ret=data[head];
            head++;
            if(head>=data.length){
                head=0;
            }
            size--;
            this.notify();
            return ret;
        }
    }
}

public class Demo12 {
    public static void main(String[] args) {
        MyBlockQueue queue=new MyBlockQueue(1000);

        Thread producer=new Thread(()->{
            int n=0;
            while(true){
                try {
                    queue.put(n+"");
                    n++;
                    System.out.println("生产元素"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"producer");


        Thread consumer=new Thread(()->{
            while(true){
                try {
                    String  n=queue.take();
                    System.out.println("消费元素"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"consumer");
        producer.start();
        consumer.start();
    }
}

线程池

对比常量池来看,字符串常量,在Java程序运行最初的时候就构建好了,等程序运行的时候,这些常量就加载到内存中;后续使用这些字符串时,直接从内存中获取,避免了字符串构造/销毁的开销。
线程池也是这样,提前创建好要使用的线程,以后要用的时候就能减少每次创建、销毁线程的损耗,提高性能。

<举个例子>其实大家可以带入“鱼塘”,想吃鱼了,捞一条上来。鱼塘里很多鱼,减少了外出购买的时间,是不是降低了损耗...

为什么从线程池去线程更高效?

首先,一个操作系统是它的内核与配套的应用程序组成。内核给应用程序提供稳定的运行环境,同时管理硬件设备。
创建线程需要操作系统内核的配合,但是内核只有一个,如果同时出现多个要求创建线程的需求,这个时候还能保证高效吗?但是如果从线程池中取现成的线程,就不需要内核的参与,就不会出现“拥挤”的情况

<举个例子>使用线程池就是去银行ATM机上取钱,创建线程是去银行柜台取钱。生活中,我们都知道ATM的效率高。

所以,使用线程池就可以省下应用程序切换到内核运行这样的消耗。

标准库中的线程池

核心方法:submit(Runnable)
通过Runnnable描述一段要执行的任务,通过submit把任务放到线程池中,此时线程池里的线程就会执行任务。本质上还是生产者消费者模型,submit在生产任务,线程池在消费任务。
在这里插入图片描述

构造这个类时,构造方法比较复杂,它的参数特别多…
主要看参数最多的构造方法,掌握整个其他的也就拿下了。

在这里插入图片描述

BlockingQueue workQueue:线程池允许我们程序员自己传一个工作队列,这可操作性就很高,我们可自主选择队列的基本参数,包括它的底层实现。
ThreadFactory threadFactory:创建线程的工厂,设计模式的一种,和前面的单例模式属于并列关系。为了统一构造并初始化线程。
在这里插入图片描述

工厂方法的核心:通过静态方法,把构造对象new的过程、各种初始化的过程,都封装为一个方法;提供多组静态方法,实现不同情况的构造。提供工厂方法的类就是工厂类。
在这里插入图片描述

RejectedExecutionHandler handler:拒绝策略,入队时,队列满了,不会真的造成阻塞(拒绝等待),而是执行下面拒绝策略的代码(提高效率)
在这里插入图片描述

实现线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool{
    private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    public MyThreadPool(int n){
        for(int i = 0; i<n; i++){
            Thread t=new Thread(()->{
                while(true){
                    try {
                        Runnable runnable=queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }

    }
}

public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool=new MyThreadPool(4);
        for(int i=0;i<100;i++){
            pool.submit(()->{
                System.out.println("hello "+Thread.currentThread().getName());
            });
        }
    }
}

完结


可以点一个免费的赞并收藏起来~
可以点点关注,避免找不到我~ ,我的主页:optimistic_chen
我们下期不见不散 ~ ~ ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值