Java_多线程

并发和并行

并发

在同一时刻,有多个指令在单个CPU上交替执行

并行

在同一时刻,有多个指令在多个CPU上同时执行

多线程的实现方式

继承Thread类的方式

注意给线程设置名字,启动线程等操作

实现Runable的方式

自己创建一个类然后去实现Runable接口,重写run方法;

为什么会有这个方式?

因为Java只支持单继承,如果已经继承一个类了,那么就不能继承第二个类,所以有局限性。

创建一个Thread对象的时候传入创建的的重写类,start()开启线程

问题

在使用自己实现的Runable接口的类的时候,不能直接使用Thread类中的方法,可以通过Thread.currentThread()获得当前执行run()函数的线程。

利用Callable接口和Future接口方式

为什么要实现Callable接口?因为之前使用的多线程的函数中并没有返回值。

1、定义的类实现Callable接口,并重写call()方法,Callable中的泛型为返回值的类型

2、创建Future类,它是一个抽象类所以应该创建它的子类FutureTask类来管理结果;创建的时候传入实现Callable接口的对象。

3、创建Thread类的时候传入FutureTask的对象

4、执行Thread类中的start()方法

4、最后通过FutureTask对象获得结果

多线程中常用的成员方法

线程优先级

对于抢占式的线程,具有优先级。Java中优先级为1-10,创建一个线程默认为5;

优先级越高,在CPU上执行的概率越高。

守护线程

当其他非守护线程执行完毕之后,守护线程会陆续结束;

换个说法就是,正常线程执行完毕了,守护线程就没有存在的必要了

定义一个非守护线程

定义一个守护线程

创建对象,并设置守护线程

结果:非守护线程结束之后,守护线程也结束了(并不是马上,存在一定的延迟)

为什么会有守护线程

当把聊天窗关闭之后,传输文件的线程就没有存在的必要了。

礼让线程

Thread.yield()为线程抢到CPU执行权之后,让出使用权。重新抢线程。

仅仅会使两个线程执行的更加均匀,并不是绝对均匀,因为还要抢。

插入线程(了解)

有两个线程A和B,B的线程中获得A线程的对象,并且A线程的对象调用join()函数,代表将A线程插入到当前B线程之前;所以A执行完毕之后B才执行。

线程的生命周期

线程的安全问题

就是3个线程卖票,然后出现卖重复和多余的票的问题。没啥好解释的。

同步代码块

为了保证操作的唯一性,解决上面线程的安全问题所提出来的

java 复制代码
synchronized(锁对象){

}
这里锁对象可以理解为之前的flag标记,只不过注意是唯一的就行

改进

字节码对象可以使用当前类的字节码文件对象

同步方法

如果是想锁住 一整个方法,那么使用同步方法;也就是将synchronized加到方法上,

1、同步方法是锁住方法里面所有的代码

2、锁住的对象不能像上面同步代码块那样能够自己指定;对于非静态的方法中,锁住的对象为this;对于静态的方法为当前类的字节码文件对象

3、技巧:对于那些不知道什么东西应该放到同步方法中的问题,可以先写同步代码块,然后将同步代码块放到同步方法中。

使用实现Runable的接口的方式进行展示

注意的点

这里面有一个要注意的地方就是,之前使用类去继承Thread类的时候,里面的ticket是static的;

但是如果是实现Runable接口,那么ticket就不用定义为static;

因为如果继承Thread的话,我们创建几个线程就创建几个Thread,所以里面的ticket为static;

我们如果实现Runable的话,我们是创建几个Thread的时候输入实现Runable的对象,所以不用为static;


把他改成同步方法的形式

lock锁

之前的就是很省事,这个lock就是很繁琐,像是原本OS中精细化上锁,解锁。还要考虑其他进程等待其他进程解锁的逻辑
下面这个函数,会发生即使票卖出100个,但是程序还是不会停止;因为在100处直接break了,没有进行解锁的操作;导致其他线程一直等待锁

因此改成try catch finally的形式,在finally中执行释放锁的操作:

死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

生产者消费者(等待唤醒机制)

就是那个一个桌子,一个生产者一个消费者的情形。

生产者

java 复制代码
package com.waitandnotify;

public class Cook extends Thread{
    @Override
    public void run() {
        synchronized (Desk.lock){
            while (true){
                if(Desk.count == 10){
                    break;
                }
                if(Desk.flag == 0){
                    Desk.flag = 1;
                    Desk.lock.notifyAll();
                    System.out.println("做了第"+Desk.count+"份");
                }else {
                    try {
                        Desk.lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

消费者

java 复制代码
package com.waitandnotify;

public class Foodie extends Thread{
    @Override
    public void run() {
        synchronized (Desk.lock){
            while(true){
                if(Desk.flag == 1){
                    System.out.println("正在吃:"+Desk.count);
                    Desk.count++;
                    Desk.flag = 0;
                    //唤醒进程
                    Desk.lock.notifyAll();
                }else {
                    try {
                        Desk.lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                if(Desk.count == 10){
                    break;
                }
            }
        }
    }
}

测试类

java 复制代码
package com.waitandnotify;

public class Demo {
    public static void main(String[] args) {
        Cook cook = new Cook();
        Foodie foodie = new Foodie();
        cook.start();
        foodie.start();
    }
}

等待唤醒机制(阻塞队列实现)

阻塞队列的继承结构

放的进程没有空间的时候会阻塞,取的进程没有空间的时候会阻塞;这就是通道啊。

ArrayBlockingQueue底层是数组,所以在创建的时候要指定大小。

创建阻塞队列

实现生产者

注意阻塞队列都是一个,所以定义一个成员变量;

注意,queue.put()个queue.take()中才是线程同步的代码;run函数中其他部分不是同步的代码;

所以输出的地方会出现重复的东西。

消费者

线程的状态

线程池

为什么会有线程池?

例子:吃饭,A吃饭要买碗,吃完就把碗摔了;线程创建和消除也是这样,很费资源。



自定义线程池


核心线程,一直存在;临时线程为当等待的任务占满了队伍,并且有新的任务的时候创建临时进程;其中定义了临时的时间,如果超过定义的临时时间没有使用临时进程,那么临时进程就销毁。如果提交的任务数量超过核心线程数量+临时线程+队伍长度;触发任务拒绝策略。

任务拒绝策略



多线程额外扩展

看阿伟的文件,不过找不到,直接看八股文吧。

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔1 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说6 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json