IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java多线程 -> 正文阅读

[Java知识库]Java多线程

目录

一 、?进程 &?线程

二、多线程的实现

2.1、继承Thread

2.2、实现Runnable接口

2.3、实现Callable接口

三、Therad类

四、线程安全问题

4.1、同步代码块——synchronized

4.2、同步方法——synchronized

4.3、显示锁——ReentrantLock()

五、线程死锁

六、wait() && notifyAll()

七、线程池——ExecutorService

7.1、缓存线程池——newCachedThreadPool()

7.2、定长线程池——newFixedThreadPool(2)

7.3、单线程线程池——newSingleThreadScheduledExecutor()

7.4、周期任务定长线程池——newScheduledThreadPool(2)



一 、?进程 &?线程

? ? ? ? ? ? ? 在学习多线程之前,我们需要先了解一下?进程?与 线程 之间的关系

? ? ? ?例子:一个能播放音频 + MV的音乐播放器。

  • ? ? ? ? 这个播放器,就是一个进程
  • ? ? ? ??播放音频、播放MV就是这个进程中的两个线程?。

? ? ? ?


? ? ? ? ? ?进程 与 线程 的占用空间概述

  • ?进程:?是指一个内存中运行的应用程序 ;各个进程都是有独立的内存空间。
  • ?线程:是进程中的一个执行路径,一个进程可以有多个线程。同一个进程里面的线程,? ? ? ? ? ? ? 可以随意切换、执行;共用一个堆内存,有各自的栈空间。

? ? ? ? ?一个进程里面,至少有一个线程;没有的话,该进程就结束运行。


? ? ? ? 线程的调度问题

? ? ? 假设电脑有8个CPU?,也就是可以同时做8件事;

? ? ? 问题来了:? 打开任务管理器,可以发现电脑上有一千多个线程!!! 明明就只能同时做8件事怎会有这么多线程呢??

? ? ?解决方法:? 为了让电脑的8个CPU“同时”处理一千多个线程(注意双引号)。这就需要合理使用电脑CPU。即调度? (调度分为两种)
?
? ? ①分时调度:

? ? ? ? ??把8个CPU的使用时间,均分给电脑全部的线程,(平均分配每个线程占用CPU的时间)? 所以,在一个极短的时间段里,8个CPU,只能执行8个线程

? ?②抢占式调度:

? ? ? ? Java使用的就是?抢占式调度,当CPU空闲时,各个线程就去争抢该CPU,抢到即开始执行线程。? 不过,Java给各个线程设置了优先权,优先级高的,就会先一步获得CPU的使用权。同一等级的,就如上面所言,去争抢CPU。(具有一定的随机性)
?


? ? ? ?在了解完?线程的调度,可能大家还不能明白上面的问题是怎么解决的。下面在详细说一下:? ? ?

? ? ? ? ? ? ?①前提:8个CPU,因此只能同时执行8个线程。

? ? ? ? ? ? ?②抢占式调度,在实际过程中,线程A只执行了?一个“极小”的时间段,然后该CPU又被其他线程抢去,也是执行了一个“极小”的时间段,又被另一个线程抢去......,好不容易线程A再次抢到该CPU,又只是执行了?一个“极小”的时间段,又被其他线程抢去.....,如此循环,直到线程A执行完毕。

? ? ? ? ? ?③所以说,①的前提,要更改成? ?“极小的时间段”里,8个CPU,只能同时执行8个线程。

? ? ? ? ? ?④我们对于这个“极小的时间段”感知不到,只是看上去这8个CPU,“同时”执行了一千多个线程而已。

二、多线程的实现

? ? ? 在大致了解了线程的执行之后,我们下一步就开始学习在Java中如何去编写多线程。目前,有3种方法,下面来逐一介绍。

? ? ? 且main方法,就相当于我Java程序里面的主线程。

2.1、继承Thread

? ?①? 继承Thread以实现多线程的流程:

  • ? ? ? ?编写一个类,继承Thread,然后实现Thread类里面的run()方法。
  • ? ? ? ?之后创建该类的对象时,一个对象?相当于?一个子线程
  • ? ? ? 创建对象之后,不是调用里面的run()方法,而是调用start()方法,来启动?子线程。

? ? ? ?

? ? ? ?该方法弊端:?这个线程?与?任务(执行的代码快)绑定在一起。就是创建n个线程,他们都是执行同一个任务。

package com.qjx;
public class Therad_Test {
    public static void main(String[] args) {
       MyThread m = new MyThread();
       m.start();
        for (int i = 0 ; i<5;i++){
            System.out.print("主线程"+i+"\t");
        }
        System.out.println();
    }
}

class MyThread extends Thread {
    /**
     * run方法,就是 线程要执行的任务方法
     *    这里的代码 就是一条新的 执行路径
     *     该执行路径的 触发方式 : 不是调用run()方法,而是通过thread对象的start()来启动任务
     *
     */
    @Override
    public void run() {
        for (int i = 0 ; i<5;i++){
            System.out.print("子线程"+i+"\t");
        }
        System.out.println();
    }
}
/**
 * 这里 mian 和  m  就是两个线程   ;
 *     互相抢占CPU, 只有两个线程  都完成时,程序才会结束
 *     每个线程都有 自己的 栈空间; 但同一份 堆内存
 */


2.2、实现Runnable接口

② 实现Runnable接口以实现多线程的流程:

  • ? ? ?编写一个类,实现Runnable,然后实现run()方法。
  • ? ? 然后创建该类的对象 (创建任务对象)
  • ? ? 创建线程,并把任务对象作为参数传递
  • ? ?调用start()方法,执行线程


实现Runnable接口 ?与 继承Thread ?相比,有如下优势:

  1. ? ?Runnable 通过创建任务, ?然后给线程分配任务 以实现多线程; 更适合多个线程同? ? ?时执行相同任务的情况
  2. ? ?可以避免 单继承 所带来的局限性(java单继承;多实现接口)
  3. ? ? 任务与线程 ? 两者是分离的,提高了程序的健壮性
  4. ? ? 后续学习的 线程池技术,接收Runnable类型的任务;不接受Thread类型的线程
    ?
package 常用类库.多线程;

public class 多线程02 {
    public static void main(String[] args) {

        /**
         *  1.  创建一个任务对象
         *  2.  创建一个线程,并且施以 任务对象
         *  3.  执行该线程
         */
        MyRunnable  r2 = new MyRunnable();
        Thread t = new Thread(r2);
        t.start();
        for (int i = 0 ; i<4;i++){
            System.out.println("第一线程"+i);
        }
    }
    }


class  MyRunnable implements  Runnable{

    @Override
    public void run() {
        for (int i = 0 ; i<4;i++){
            System.out.println("第二线程"+i);
        }
    }
    }

class  My implements  Runnable{

    @Override
    public void run() {
        for (int i = 0 ; i<4;i++){
            System.out.println("第三线程"+i);
        }
    }
}

2.3、实现Callable接口

③ 实现Callable接口,以实现多线程的流程:

  • ? ? ?编写一个类,实现Callable接口,实现Callable类里面的call()方法。并创建该类对象A
  • ? ? ?通过FutureTask类,创建任务对象B,并把A作为参数传递
  • ? ? ?创建线程对象C,并把B作为参数传递,调用start()方法启动线程? ??

? ? ? ? ? ? ?实现Callable接口、创建对象A时,是需要传入一个泛型值的。


? 与前两种方法的区别:

  • ? ? ? ? ? 前两种 :?子线程 &?主线程,是两个执行路径,“同时”执行的。
  • ? ? ? ? ? 第三种 :?执行子线程时,主线程会暂时停止,等子线程执行完毕,返回一个值,主线程才重新启动。 (也可以像?前两种一样?与主线程同时执行)。

? ? ? 当然,这个特性,是需要调用get()方法,才会这样。调用了,主线程才会等子线程执行完毕,再执行。(前提:主线程的代码 必须在.get()方法的后面。 在前面,则是主线程先执行完毕,再去执行子线程)?get()方法会返回 Object类型的数据

? ? 没有调用,子线程与主线程 “同时”执行。

package com.qjx;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class Callable_Test {
    public static void main(String[] args) throws InterruptedException {
        Callable<String> m = new My();
        FutureTask f = new FutureTask(m);   //创建任务
        Thread t = new Thread(f);           //创建线程
        t.start();                          //启动线程
        try {
            Object s = f.get();             //调用get()方法
            System.out.println(s);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        for (int i =0; i<4; i++){
            Thread.sleep(1000);
            System.out.println("主线程:\t"+i);
        }
    }
}

class My implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i=0; i<4;i++){
            Thread.sleep(100);
            System.out.println("子线程:\t"+i);
        }
        return "该子线程 已经完成 结束";
    }
}

三、Therad类

? ? ? ? 通过上面?三种实现多线程的方法中,我们可以看到三者都涉及了 Thread类;下面我就来介绍Thread类。

? ? ? ?Thread常见的构造方法:

  1. ? ? ? Thread()? ? //该线程?还没有任务
  2. ? ? ? Thread(Runnable? target)? ? //给该线程,赋予一个任务
  3. ? ? ? Thread(String? name)? ? ? ? ?//给该线程?一个名字
  4. ? ? ? Thread(Runnable target? ,? String? name)? ? //给线程任务? +?名字

? ? 获取 \ 设置线程名字? ?&&获取线程标识符

  • setName()? ?// 设置线程名字
  • getName() // 返回线程 名字?
  • getId() //返回Thread标识符
  • Thread.currentThread() // 获取 当前线程对象
public class Thread_Test {
    public static void main(String[] args) {
        MyRunnable m1 = new MyRunnable();
        Thread t1 = new Thread(m1,"旧名字");
        System.out.println(t1.getName());
        t1.setName("新名字");

        t1.start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
        System.out.println(Thread.currentThread().getId());
        System.out.println("hello");
    }
}

?线程休眠 ——sleep

 sleep(long millis) //让线程暂时休眠    millis 毫秒
 sleep(long millis, int nanos)     mills毫秒 + nanos纳秒
/**
 *  线程 休眠  sleep
 *  需要 抛出异常  or 处理异常
 */
public class Thread_Test {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i<5; i++){
            System.out.println(i);
            Thread.sleep(1000); //休眠1s ,程序才会继续执行下去
        }
    }
}

线程阻塞

? ? ? 线程阻塞,不单只是说?线程休眠。

? ? ??而是所有?耗时的操作,都是线程阻塞。

? ? ? 比如说,线程在执行IO操作时,需要读取一个文件里面所有的数据;读取完数据,该线程才会往下执行,那这个读取时间,也就是阻塞时间。

线程中断——interrupt

? ? ? ?一个线程是一个独立的执行路径,它是否结束,应该由其自身决定

? ? ? ?一个线程可能有许多资源,外部直接“杀死"它,可能导致这些资源―不能释放。占用空间,产生垃圾(可能是还不能回收的垃圾)

? ? ???因此主要需要?以线程中断方式,去结束线程。


  • ? “线程名.interrupt();"??//给该线程添加? 中断标记
  • ?可以理解为,线程本来就有中断标记,这一句代码,就是给该标记赋值为 true
  • ?当子线程进行某些操作时(sleep、wait),就会去检查这个中断标记;当中断标记? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 为?true?就抛出异常InterruptedException。
  • ? 然后,异常处理?catch里面? 可以根据需要,去“杀”或者“不杀”该线程。? ?"?return ;"? ?就表示 “杀死”线程。
      如果子线程没有某些操作时,即使标记为true,也不会中断。
public  class Thread_Test {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable m1 = new MyRunnable();  //创建任务
        Thread t1 = new Thread(m1);  //创建  线程
        t1.start();
        for (int i = 0; i<5; i++){
            System.out.println("主 ---"+i);
            Thread.sleep(1000);
        }
        // 给子线程  添加中断标记  .
        t1.interrupt();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i<10; i++){
            System.out.println("子 ---"+i);

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("主线程已经执行完毕,检测到中断标记,是否中断子线程,请决定操作:  1--中断    2--继续");
                int num = new Scanner(System.in).nextInt();
                if (num == 1){
                    return;
                }
            }

        }
    }
}

守护线程——setDaemon(true)

前面说过,进程所有线程结束时,该进程就over 了

现在细说:? ?线程分为-----用户线程-----守护线程

  • 用户线程:? 我们前面所看到的―主线程、子线程(直接创建的线程都是用户线程)
  • 守护线程:? 我们利用"用户线程名.setDaemon(true)",该用户线程就变成了守护线程。

接着细说:? ?

  • ? ? ? ? ? ? ? ? ? 进程? 所有 用户线程结束时,该进程结束
  • ? ? ? ? ? ? ? ? ? 最后一个用户线程结束时,守护线程自动死去

public  class Thread_Test {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable m1 = new MyRunnable();  //创建任务
        Thread t1 = new Thread(m1);  //创建用户线程
        t1.setDaemon(true);   ///  设置为  守护线程
        t1.start();
        for (int i = 0; i < 6; i++){
            System.out.println(i);
            Thread.sleep(1000);
        }

    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        while (true){  //守护线程  一直执行。直到用户线程(该程序中,指主线程main)结束时,死亡
            System.out.println("我是--守护线程---随时等待死亡");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}



四、线程安全问题

? ? ? ? Java多线程虽然可以提高工作效率,但是也容易出错。

? ? ? ? ?多个线程,执行同一个任务时,就容易出错!!!!

可以看到执行结果,出现余票为负数的情况:

原因就是:

  • ? ??线程A判断的时候,count符合条件﹐
  • ? ? 但是下一个“极小”的时间段里,另一个线程B抢到了CPU执行"count--”,以致count小于0 ;
  • ? ? 然后线程A就输出了不满足条件的count。
    ?
public class ThreadTest {

    public static void main(String[] args) {
        MyRunnable m = new MyRunnable();
        new Thread(m).start();  //第 一 个线程  执行下面的任务
        new Thread(m).start();  //第 二 个线程  执行下面的任务
        new Thread(m).start();  //第 三 个线程  执行下面的任务
    }

}
  class MyRunnable implements Runnable{
    private  int count = 10;
    @Override
    public void run() {

        while (count > 0){
            System.out.println("正在卖票:");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println("出票成功,余票:"+count);
        }
    }
}

? ?为了解决上面的问题,Java给出了3种方法来解决问题。

4.1、同步代码块——synchronized

格式:

? ? ? ?synchronized(锁对象){? ? ? ? // Java中所有对象,都可以作为锁对象

? ? ? ? ? ? ? 代码块

? ? ? }


理解:

  • ? ?? ? ? 当线程A,执行到synchronized部分的代码时,就会去关注,锁对象是否已经上锁(上锁,就表示有一个线程,正在执行这一部分代码)。
  • ? ? ? ? 如果已经上锁,线程A就只能等待,等待锁对象?解锁(解锁,就表示这一部分代码,没有线程在执行)。
  • ? ? ? ? 没锁、解锁的情况下,线程A又必须与其他线程去争抢这个锁。

class MyRunnable implements Runnable{
    private  int count = 10;
    private Object o = new Object();  //锁对象
    @Override
    public void run() {

            while (true){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o){
                    if (count > 0){
                        System.out.println("正在卖票:");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"\t出票成功,余票:"+count);
                    }else {
                       break;
                    }
                }
            }
    }
}

4.2、同步方法——synchronized

? 同步方法,在编写一个方法时,加上“synchronized”。效果如同步代码块一样。

class MyRunnable implements Runnable{
    private  int count = 10;
    @Override
    public void run() {
         while (true){
             boolean f = sale();
             if (!f){
                 break;
             }
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
    }
    public  synchronized boolean sale(){
        // 该方法也是有 锁对象的,谁调用该方法 ,就是ta
        if (count > 0){
            System.out.println("正在卖票:");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
            return true;
        }
        return false;
    }
}

4.3、显示锁——ReentrantLock()

同步代码块、同步方法都是隐式锁。??下面我们来了解一下隐式锁。

格式:?private Lock l = new ReentrantLock();? ?//创建锁对象? ,传入参数true,变公平锁

? ? ?l.lock()? //表示上锁,其他线程不能进入,得等待?解锁

? ? ?l.unlock()? //表示解锁,线程之间可以开始争抢锁

class MyRunnable implements Runnable{
    private  int count = 10;
    // 显示锁
    private Lock l  = new ReentrantLock();  //

    @Override
    public void run() {
        while (true){
            l .lock();  //
            if (count > 0){
                System.out.println("正在卖票:");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"出票成5功,余票:"+count);
            }else {
                break;
            }
            l.unlock();  //
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}


五、线程死锁

public class ThreadTest {
    public static void main(String[] args) {
       Culprit c = new Culprit();
       Police p = new Police();
       new MThread(c,p).start();
       c.say(p);
    }
}

class MThread extends Thread{
    private Culprit c ;
    private  Police p ;
    public MThread(Culprit c , Police p ){
        this.c = c;
        this.p = p;
    }
    @Override
    public void run() {
        p.say(c);
    }
}

/**
 * 罪犯类
 */
class Culprit{
    public synchronized void say(Police p){    //罪犯 说话
        System.out.println("罪犯:给我五百万+飞机;我就把人质放了!");
        p.fun();
    }

    public synchronized void fun(){   //罪犯 回答 警察
        System.out.println("罪犯的回应:那就准备1百万");
    }
}

/**
 * 警察类
 */
class Police{
    public synchronized void say(Culprit c){    //警察说话
        System.out.println("警察:不可能,你赶紧放了人质");
        c.fun();
    }

    public synchronized void fun(){     //警察  回答 罪犯
        System.out.println("警察的回应:okokok!一百万");
    }
}

解析:? 执行完主线程①时,但还没执行②时 ,子线程的①已经执行。? ??

? ? ? ? ?(因为①到②这一段过程,是“极小的时间段”,你可以暂时理解为? 主、子线程的①是“同时”进行的。方便理解)

主线程:c.say (p )

? ? ? ? ? ? ? ?①?打印了语句;此时c是锁住的? ??

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (极小时间段)

? ? ? ? ? ? ? ? ? ? ? ? ? ?②执行p.fun ()语句,但是此时p已经锁住了,进不去,也就调用不了fun方法


子线程: p.say(c)

? ? ? ? ? ? ? ?①打印了语句;此时p是锁住的

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (极小时间段)

? ? ? ? ? ? ? ? ? ? ? ? ?②执行c.fun ()语句,但是此时c已经锁住了,进不去,也就调用不了fun方法


?结果 :? 就锁死了;因为C\P都锁住了,C\P里面的代码就执行不下去,C\P都一直在等锁开。

?当然,也有可能不出现死锁的情况,那就是主线程①、②执行完时,子线程才开始执行①。


避免线程死锁的方法:

? ? ? ?在任何有可能导致锁产生的方法里(产生锁的方法)? ??

? ? ? ?不在调用另一个可能产生锁的方法

? ? (比如:上面代码中,say()方法? 调用了?另一个类的fun()方法)



六、wait() && notifyAll()

AP文档中,Object类里面有两个方法,wait()? && notifyAll()。

  • ? wait() :? ?让当前线程?睡眠,直到被唤醒。?

? ? ? ? ? ? ? ? ? ? ? ?参入参数,也是等待被唤醒,或者超过时间参数,自己唤醒。

  • notifyAll() :?唤醒等待此对象监视器的所有线程

? 例子:?线程A ------ 厨师? ? ? ? ? ? ? ? ? ? ? 线程B-----?服务员

? ? ? ? ? ? ? ? 任务? ------ 做菜? ? ? ? ? ? ? ? ? ? ? ??任务------ 上菜

? ?原则上,是厨师做完一道菜 ,服务员就把菜端走;厨师再做一道菜。

? ?问题就是:?代码实现时,这两个线程,会抢着执行。所以就会出现下面两个问题

  • ? ? ? ? ? ? ? ? ?服务员还没把菜端走,厨师就又做了一道菜。?
  • ? ? ? ? ? ? ? ? ?或者厨师还没做下一道菜,服务员这边就又要上菜了。

? ?因此,就需要利用上面两个方法,让厨师做完一道菜时,进入睡眠状态,直到被唤醒;服务员上菜之后,进入睡眠状态,直达被唤醒。

public class ThreadTest {
   
    public static void main(String[] args) {
        Food f = new Food();  //创建food对象  f
        new Cook(f).start();  //启动 厨师线程
        new Waiter(f).start();  //启动 服务员线程
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() { 
            for(int i=0;i<100;i++){               //厨师的任务,包括setNameAndSaste()
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }


    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();               // 服务员的任务 包括get()方法
            }
        }
    }



/**
*   食物,上面厨师的任务setNameAndSaste  , 服务员的任务get。 都针对于食物
*         因此写成一个food类
*
*/
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();   //唤醒  睡眠的线程,这里就是唤醒服务员线程
                try {
                    this.wait();    //  厨师线程  睡眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }



        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();   //唤醒  睡眠的线程,这里就是唤醒 厨师线程
                try {
                    this.wait();    //服务员线程 睡眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



七、线程池——ExecutorService

? ? ? 后续编程中,我们可能会创建大量线程,但这些线程只执行一次,就不再使用。

? ? ? ?而且,创建线程? &&? 执行任务,两段占用程序的时间里,执行任务可能只占据总时间的5%,大量时间浪费于线程的创建。这样就会降低系统的效率。


线程池:就是线程的容器。

? ? ? 因此,Java引入了线程池的概念,线程池里面可以容纳多个线程,且线程可以重复使用。节省了频繁创建线程所需的时间、资源。

线程池的优点:? ?

  • ? ? ? 降低资源消耗。
  • ? ? ? 提高响应速度。
  • ? ? ? 提高线程的可管理性。

? ? ? ? ?

7.1、缓存线程池——newCachedThreadPool()

长度无限。

执行流程:

  • ? ? ? ? ? ? 判断线程池是否存在空闲线程 ,
  • ? ? ? ? ? ? 存在则使用空闲线程,
  • ? ? ? ? ? ? 不存在,则创建新线程 并放入线程池, 然后使用,新创建的线程也会重复使用

使用格式:

? ExecutorService service = Executors.newCachedThreadPool();? //创建线程池

?service.execute(Runnable?traget)? //执行任务,线程池就会去调用/创建线程,执行该任务

7.2、定长线程池——newFixedThreadPool(2)

长度指定好。

执行流程:

  • ? ? ? ? ? ? 判断线程池是否存在空闲线程
  • ? ? ? ? ? ? 存在则使用
  • ? ? ? ? ? ?不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  • ? ? ? ? ? ?不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

使用格式:

ExecutorService service = Executors.newFixedThreadPool(2);? //创建线程池,指定长度

service.execute( Runnable? target )? //执行任务


? ? ? ?可以看见,定长线程池? &&?缓存线程池,创建方式基本一致。?定长线程池,只是指定了线程池里面可以存放多少个线程。

7.3、单线程线程池——newSingleThreadScheduledExecutor()

?单线程线程池的效果? ?===? ?定长线程池指定长度为1时。

?执行流程:

  • ? ? ? ?判断线程池 的那个线程 是否空闲
  • ? ? ? ?空闲则使用
  • ? ? ? ?不空闲,则等待 池中的单个线程空闲后 使用

使用格式:

ExecutorService service = Executors.newSingleThreadScheduledExecutor();?

service.execute(Runnable? target)

7.4、周期任务定长线程池——newScheduledThreadPool(2)

周期性任务执行时:

? ? ? ? ? ?定时执行, 当某个时机触发时, 自动执行某任务 。


执行流程,与定长线程池一样。


使用格式:

ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

service.schedule()

?① 定时执行一次 :? ??

? ? ? 参数1 : 定时执行的 任务?

? ? ? 参数2 : 时长数字? ?,多长时间后执行任务。

? ? ? 参数3 : 时长数字的 时间单位,TimeUnit的常量指定?

? ? ? service.schedule(?target , 5 ,TimeUnit.SECONDS)? ?5秒后,执行target任务

② 周期执行:

? ? ? 参数1 : 定时执行的 任务

? ? ? 参数2 : 延迟时长数字 (第一次执行 在什么时间以后)

? ? ? 参数3 : 周期时长数字 (多久执行一次)

? ? ? 参数4 : 时长数字的 时间单位,TimeUnit的常量指定

service.schedule(?target , 5 ,1,TimeUnit.SECONDS)? ?5秒后,开始执行target任务,之后每隔1秒,执行一次target任务。

?感谢您的观看!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:19:02  更:2021-08-31 15:21:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:32:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码