基础复习(多线程)

news2024/11/12 12:47:51

线程创建方式

1.继承Thread类

2.实现Runable接口

3.Callable接口实现有返回值的线程

(1)第一种

提供了一个类叫做Thread,此类的对象用来表示线程。创建线程并执行线程的步骤如下

1.定义一个子类继承Thread类,并重写run方法
2.创建Thread的子类对象
3.调用start方法启动线程(启动线程后,会自动执行run方法中的代码)

eg:

public class FirstThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程执行第"+i+"次");
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Thread t = new FirstThread();
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程开启第"+i+"次");
        }
    }
}

(2)第二种

提供了一个Runnable接口,该接口中只有一个run方法,意思就是通过Runnable接口的实现类对象专门来表示线程要执行的任务。具体步骤如下

1.先写一个Runnable接口的实现类,重写run方法(这里面就是线程要执行的代码)
2.再创建一个Runnable实现类的对象
3.创建一个Thread对象,把Runnable实现类的对象传递给Thread
4.调用Thread对象的start()方法启动线程(启动后会自动执行Runnable里面的run方法) 

 优化:不想写Runnable实现类,可以直接创建Runnable接口的匿名内部类对象,传递给Thread对象。

public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("创建的线程");
                }
            }
        };
        new Thread(r).start();

(3)第三种 

假设线程执行完毕之后有一些数据需要返回,前面两种方式重写的run方法均没有返回结果

JDK5提供了Callable接口和FutureTask类来创建线程,它最大的优点就是有返回值。

在Callable接口中有一个call方法,重写call方法就是线程要执行的代码,它是有返回值的。

1.先定义一个Callable接口的实现类,重写call方法
2.创建Callable实现类的对象
3.创建FutureTask类的对象,将Callable对象传递给FutureTask
4.创建Thread对象,将Future对象传递给Thread
5.调用Thread的start()方法启动线程(启动后会自动执行call方法)
   等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中
6.调用FutrueTask对的get()方法获取返回结果 

 假设已经有了Callable的实现类,后步骤如下:

public static void main(String[] args) throws Exception {
        // 3、创建一个Callable的对象
        Callable<String> call = new MyCallable(100);
        // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现了Runnable对象.
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
        FutureTask<String> f1  = new FutureTask<>(call);
        // 5、把任务对象交给一个Thread对象
        new Thread(f1).start();


        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> f2  = new FutureTask<>(call2);
        new Thread(f2).start();


        // 6、获取线程执行完毕后返回的结果。
        // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2);
    }

多线程常用方法 

先获取线程对象(currentThread())才能进行线程相关操作。 

线程安全问题 

多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。 (取钱问题)

线程同步方案 

每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动释放锁,然后其他线程才能再加锁进来。

加锁提供了三种方案

1.同步代码块
2.同步方法
3.Lock锁

同步代码块 

访问共享数据的代码放入

//锁对象:必须是一个唯一的对象(同一个地址)
synchronized(锁对象){
    //...访问共享数据的代码...
}

示例: 

// 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    // this正好代表共享资源!
    synchronized (this) {
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }

对象选择问题

1.建议把共享资源作为锁对象, 不要将随便无关的对象当做锁对象
2.对于实例方法,建议使用this作为锁对象
3.对于静态方法,建议把类的字节码(类名.class)当做锁对象 

同步方法 

同步方法,就是把整个方法给锁住,一个线程调用这个方法,另一个线程调用的时候就执行不了,只有等上一个线程调用结束,下一个线程调用才能继续执行。

// 同步方法
public synchronized void drawMoney(double money) {
    // 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    if(this.money >= money){
        System.out.println(name + "来取钱" + money + "成功!");
        this.money -= money;
        System.out.println(name + "来取钱后,余额剩余:" + this.money);
    }else {
        System.out.println(name + "来取钱:余额不足~");
    }
}

同步方法也是有锁对象,只不过这个锁对象没有显示的写出来而已。
    1.对于实例方法,锁对象其实是this(也就是方法的调用者)
    2.对于静态方法,锁对象时类的字节码对象(类名.class) 

区别

1.不存在哪个好与不好,只是一个锁住的范围大,一个范围小
2.同步方法是将方法中所有的代码锁住
3.同步代码块是将方法中的部分代码锁住 

Lock锁 

Lock锁是JDK5版本专门提供的一种锁对象,通过这个锁对象的方法来达到加锁,和释放锁的目的,使用起来更加灵活。格式如下:

1.首先在成员变量位置,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
	private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
	 lk.lock(); // 加锁
	 //...中间是被锁住的代码...
	 lk.unlock(); // 解锁

线程通信 

当多个线程共同操作共享资源时,线程间通过某种方式互相告知自己的状态,以相互协调,避免无效的资源挣抢。

线程通信的常见模式:是生产者与消费者模型

线程池 

 线程池就是一个可以复用线程的技术

场景:

假设:用户每次发起一个请求给后台,后台就创建一个新的线程来处理,下次新的任务过来肯定也会创建新的线程,如果用户量非常大,创建的线程也讲越来越多。然而,创建线程是开销很大的,并且请求过多时,会严重影响系统性能。而使用线程池,就可以解决上面的问题。

创建线程池 

在JDK5版本中提供了代表线程池的接口ExecutorService,而这个接口下有一个实现类叫ThreadPoolExecutor类,使用ThreadPoolExecutor类就可以用来创建线程池对象。

 

创建线程池对象实例:

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);
  • 临时线程什么时候创建?

    新任务提交时,发现核心线程都在忙、任务队列满了、并且还可以创建临时线程,此时会创建临时线程。
  • 什么时候开始拒绝新的任务?

    核心线程和临时线程都在忙、任务队列也满了、新任务过来时才会开始拒绝任务。

 线程池执行Callable/Runable任务

实例(前提:线程任务类已准备好):

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

Runnable target = new MyRunnable();
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
//下面4个任务在任务队列里排队
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);

//下面2个任务,会被临时线程的创建时机了
pool.execute(target);
pool.execute(target);
// 到了新任务的拒绝时机了!
pool.execute(target);

线程池工具类(Executors)

快捷的创建不同特点的线程池,不推荐使用!!!

 不推荐使用原因

线程生命周期

NEW: 新建状态,线程还没有启动
RUNNABLE: 可以运行状态,线程调用了start()方法后处于这个状态
BLOCKED: 锁阻塞状态,没有获取到锁处于这个状态
WAITING: 无限等待状态,线程执行时被调用了wait方法处于这个状态
TIMED_WAITING: 计时等待状态,线程执行时被调用了sleep(毫秒)或者wait(毫秒)方法处于这个状态
TERMINATED: 终止状态, 线程执行完毕或者遇到异常时,处于这个状态。

 

 

 

 

 

 

 

 

 

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

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

相关文章

无密码sudo

文件路径&#xff1a;/etc/sudoers 修改sudoers文件 进去root 权限&#xff1a;sudo su 加入sudoers 写权限&#xff1a;chmod w sudoers 修改sudoers文件&#xff1a;vim sudoers 根据下面图片修改 wq退出编辑

华为LTC流程体系详解

LTC&#xff0c;全称Lead to Cash&#xff0c;中文翻译为从线索到现金&#xff0c;是一种企业运营管理思想&#xff0c;也是一个集成的业务流程。它涵盖了企业从接触客户到收到客户回款的整个流程&#xff0c;通过科学化管理&#xff0c;实现更高效地将线索客户转化为付费客户。…

学习web前端三大件之HTML篇

HTML的全称为超文本标记语言&#xff0c;是一种标记语言。它包括一系列标签&#xff0c;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的Internet资源连接为一个逻辑整体。HTML文本是由HTML命令组成的描述性文本&#xff0c;HTML命令可以说明文字&#xff0c;图形…

每日一面系列之美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以

ConcurrentHashMap 为什么 key 和 value 不能为 null&#xff1f; ConcurrentHashMap 的 key 和 value 不能为 null 主要是为了避免二义性。null 是一个特殊的值&#xff0c;表示没有对象或没有引用。如果你用 null 作为键&#xff0c;那么你就无法区分这个键是否存在于 Concu…

1. shiro的基本使用

一、现存问题 1. 认证&#xff08;登录&#xff09;&#xff1a;认证操作流程都差不多&#xff0c;但是每次都需要手动的基于业务代码去实现&#xff0c;很麻烦&#xff01; 2. 授权&#xff1a;如果权限控制粒度比较粗&#xff0c;可以自身去实现&#xff0c;但是如果控制粒度…

fastadmin插件市场暂不可用,是否切换到本地插件

今天调试时需要安装一个富文本插件&#xff0c;结果在插件管理模块提示如下错误&#xff1a; 经过参考网上资料&#xff0c;最终解决方案&#xff1a; 修改backend/config目录下&#xff0c;fastadmin.php 中代码&#xff1a; //API接口地址 api_url > https://api.iuok.c…

canopenfestival生成字典使用的总结

添加地图变量 1、不带索引计数的 子索引的值类型固定 2、带索引计数 子索引的值类型固定,子索引名字不可更改 3、带索引计数 子索引的值类型不固定&#xff0c;子索引名字可更改

HarmonyOS多目标产物构建最佳实践

背景 在Android或iOS开发时经常会有打“马甲”包的场景&#xff0c;就是一套代码打出不同主题的包&#xff0c;一个公司的产品可能针对不同用户提供不同的应用&#xff0c;比如抖音有国内版也有国外版&#xff0c;滴滴有个人版还有企业版&#xff0c;同样的在鸿蒙平台也有类似…

百元内性价比最高的随身WiFi!格行随身WiFiVS京东云VS先机随身WiFi真实测评!哪个随身WiFi网速最快?口碑最好的随身WiFi!

随身WiFi是大家都熟知的便携式上网设备不管是出差旅行✈还是学生党租房都非常合适。但是现在市面上的随身WiFi产品良莠不齐‼价格也千差万别&#xff01;今天给大家挑选三款百元内性价比最高的随身WiFi出期测评看下哪款最好用——先机、格行和京东云的详细测评&#xff0c;帮助…

深度学习6--深度神经网络

1.VGG网络 在图像分 类这个领域中&#xff0c;深度卷积网络一般由卷积模块和全连接模块组成。 (1)卷积模块包含卷积层、池化层、Dropout 层、激活函数等。普遍认为&#xff0c;卷积模块是对 图像特征的提取&#xff0c;并不是对图像进行分类。 (2)全连接模块跟在卷积模块之后&…

Selenium安装WebDriver:ChromeDriver谷歌浏览器驱动下载安装与使用最新版116/117/118/119/120/121/122/123

Selenium安装WebDriver&#xff1a;ChromeDriver谷歌浏览器驱动下载安装与使用最新版116/117/118/119/120/121/122/123 文章目录 Selenium安装WebDriver&#xff1a;ChromeDriver谷歌浏览器驱动下载安装与使用最新版116/117/118/119/120/121/122/1230. 背景1. 确定Chrome版本2.…

大模型系统和应用——自然语言处理大模型基础_大模型和自然语言处理的相互影响

引言 最近在公众号中了解到了刘知远团队退出的视频课程《大模型交叉研讨课》&#xff0c;看了目录觉得不错&#xff0c;因此拜读一下。 观看地址&#xff1a; https://www.bilibili.com/video/BV1UG411p7zv 目录&#xff1a; 自然语言处理&大模型基础神经网络基础Transf…

新款奔驰G350升级动态通风按摩座椅有哪些功能

奔驰大 G350 升级通风按摩座椅的作用主要包括以下方面&#xff1a; 通风座椅的作用&#xff1a; • 改善空气流通&#xff1a;在炎热天气下&#xff0c;即使车内空调温度设定较低&#xff0c;乘客坐在座椅上时&#xff0c;身体与椅面紧密接触的部分仍可能会感到闷热&#xff…

人大高瓴发布Think-on-Graph 2.0,基于知识图的大模型推理再升级!

经常参加高考的朋友可能会体会到&#xff0c;比起死记硬背知识点&#xff0c;将知识整理成脉络往往会获得事半功倍的效果。其实对于大模型来说也是如此&#xff0c;哪怕被允许“开卷作答”&#xff0c;即通过检索增强&#xff08;Retrieval-augmented generation&#xff0c;RA…

【前端面试3+1】20 css三栏布局6种实现方式、多行文本溢出怎么实现、token过期了怎么处理、【二叉树的中序遍历】

一、css三栏布局6种实现方式 1.浮动布局&#xff08;Floats&#xff09; .container {overflow: auto; /* 清除浮动 */ }.left, .right {width: 20%; /* 左右栏宽度 */float: left; }.middle {width: 60%; /* 中间栏宽度 */margin: 0 20%; /* 左右栏宽度 */ } 2.Flexbox .conta…

面试经典 222. 完全二叉树的节点个数

二叉树我最近刷的特别多&#xff0c;差不多快刷完了&#xff0c;但是有一种题型差点给我忽略了&#xff0c;那就是完全二叉树&#xff0c;这也是一个很重要的题型&#xff0c;今天刚好有一道题目可以来复习一下完全二叉树的特性 题目链接如下&#xff1a;https://leetcode.cn/…

力扣-200.岛屿数量

刷力扣热题–第二十四天:200.岛屿数量 新手第二十四天 奋战敲代码&#xff0c;持之以恒&#xff0c;见证成长 1.题目描述 2.题目解答 这道题刚开始想的确实想的绞尽脑汁的&#xff0c;看了相关解答才明白的&#xff0c;三种方法&#xff0c;这里想先用两种方法进行实现&#…

MySQL改密码(简洁无废话)

1.找到MySQL的bin目录&#xff1a; 2.在上方输入cmd&#xff1a; 进入这个界面&#xff1a; 3.输入 mysqladmin -uroot -p password 4.输入后在下面输入原密码&#xff1a; 5.下面输入新密码&#xff0c;确认新密码&#xff1a; 然后就可以修改成功了。

学习 Helm ,一文弄懂

1. 什么是 Helm 1.1 概述 Helm 是 Kubernetes 应用程序的包管理器,和redhat中yum 管理包类似. 1.2 架构图v3 1.3 下载 官当 最新版本 官方github curl -LO https://get.helm.sh/helm-v3.15.2-linux-amd64.tar.gz 1.4 安装 解压 #由于是二进制,直接解压到/usr/local/b…

科普文:解读MySQL 执行计划explain

概叙 实战&#xff1a;万字小结MySQL慢原因分析-CSDN博客 实战&#xff1a;搞懂SQL执行流程、SQL执行计划解读和SQL优化_sql解析和sql执行计划-CSDN博客 在排查mysql执行慢的过程中&#xff0c;前面文章中都有解释explain执行计划&#xff0c;这里单独拿出来解读一下。 慢查…