当前位置: 首页 > news >正文

JAVA——多线程

目录

1.多线程概述

2.多线程的创建方式

a.继承Thread类,重写run方法

b.实现Runnable接口

c.实现Callable接口

3.Thread类常见方法

a.返回此线程的名称

b.设置线程的名字

c.获取当前线程的对象

d.让线程休眠指定时间(单位:毫秒)

e.设置线程的优先级

f.获取线程的优先级

g.设置为守护线程

h.出让线程

i.插入线程

4.线程的生命周期

5.线程的安全问题

a.概述

b.同步代码块

1.概述

2.语法

c.同步方法

1.概述

2.语法

d.Lock锁

1.概述

2.常见方法

6.生产者和消费者

a.概述

b.等待唤醒机制

c.阻塞队列实现等待唤醒机制

7.线程池

a.概述

b.利用工具类创建线程池

c.自定义线程池

1.构造方法

2.底层原理

3.线程池中线程数量


1.多线程概述

线程(Thread)是一个程序内部的一条执行流程。

程序如果有一条执行流程,就是单线程程序。

多线程是指从软硬件上实现的多条执行流程的技术(多条线程由 CPU 负责调度执行)。

2.多线程的创建方式

a.继承Thread类,重写run方法

1.创建子类MyThread继承Thread线程类

2.重写Thread类的run方法

3.创建的MyThread线程类的对象代表一个线程

4.调用start()方法启动线程(自动执行run方法)

示例:


public class Main {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();for (int i = 1; i <9 ; i++) {System.out.println("主线程输出"+i);}}}class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i < 9; i++) {System.out.println("子线程输出:"+i);}}
}

运行结果:

优点:编码简单

缺点:继承了Thread类无法继承其他类,不利于功能扩展

b.实现Runnable接口

1.定义任务类,实现Runnable接口

2.重写run方法

3.创建任务对象

4.把任务对象交给线程对象处理

示例:


public class Main {public static void main(String[] args) {//创建任务对象MyRun myRun = new MyRun();//把任务对象交给线程对象处理Thread thread = new Thread(myRun);thread.start();for (int i = 1; i < 9; i++) {System.out.println("主线程输出"+i);}}
}
class MyRun implements Runnable{@Overridepublic void run() {for (int i = 1; i < 9; i++) {System.out.println("子线程输出"+i);}}
}

运行结果:

优点:扩展性强,实现该接口的同时还可以继承其他类

缺点:编程相对复杂,不能直接使用Thread类中的方法

c.实现Callable接口

概述:假如线程执行完毕后需要一些数据的返回,前面两种方式重写的 run 方法均不能返回结果。

此时可以通过 Callable 接口和 FutureTask 类来实现。

1.创建一个类MyCallable实现Callable接口

2.重写call(是有返回值的,表示多线程运行的结果)

3.创建MyCallable对象(表示多线程要执行的任务)

4.创建FutureTask的对象(表示管理多线程运行的结果)

5.创建Thread类对象,并启动(表示线程)

示例:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//多线程要执行的任务MyCallable myCallable = new MyCallable();//管理多线程运行的结果FutureTask futureTask = new FutureTask<>(myCallable);//创建线程Thread thread = new Thread(futureTask);thread.start();for (int i = 1; i < 20; i++) {System.out.println("主线程已经执行");}System.out.println("子线程运行返回结果为:"+futureTask.get());}
}
class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("子线程已执行");int sum=0;for (int i = 0; i <=100 ; i++) {sum=sum+i;}return sum;}
}

运行结果:

优点:扩展性强,实现该接口的同时还可以继承其他类

缺点:编程相对复杂,不能直接使用Thread类中的方法

3.Thread类常见方法

a.返回此线程的名称

b.设置线程的名字

注意:如果我们没有给线程设置名字,线程也是有默认名字的。格式:Thread-X(X序号,从0开始的)

c.获取当前线程的对象

注意:当JVM虚拟机启动之后,会自动的启动多条线程,其中一条线程就叫做main线程,并执行里面的代码

d.让线程休眠指定时间(单位:毫秒)

注意:哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间,当时间到了之后,线程会自动醒来,继续执行下面的代码

e.设置线程的优先级

f.获取线程的优先级

g.设置为守护线程

注意:当其他的非守护线程执行完毕之后,守护线性会陆续结束

h.出让线程

注意:当线程调用yield方法后,线程即从运行状态变为可运行状态,将CPU的执行权让给同级别的线程;让出CPU执行权之后,又同别的线程共同争夺CPU的执行权。(可能释放了CPU执行权之后 又抢到了;同时多线程之前是抢占CPU的,所以从运行结果上很难看出是否yield方法起了作用)

i.插入线程

注意:等待调用该方法的线程执行完毕之后,再执行其他线程,其内部是wait实现的,因此它会释放对象锁

4.线程的生命周期

5.线程的安全问题

a.概述

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

b.同步代码块

1.概述

将操作共享数据的代码锁起来(实现在并发执行过程中,同一时刻只有一个线程能访问程序)

2.语法

 synchronized (锁对象){执行的代码}

注意:

        1.synchronized (锁对象){执行的代码}不要写在循环的外面

        2.锁的对象一定要是唯一的

c.同步方法

1.概述

同步方法是指使用synchronized关键字修饰的方法

2.语法

 修饰符 synchronized 返回值类型 方法名(形参列表) {
// 操作共享资源的代码
}

注意:

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

         2.锁对象不能自己指定,如果方法是实例方法,默认this作为锁对象,如果方法是静态方法,默认类名.Class作为锁对象

d.Lock锁

1.概述

通过它创建出锁对象进行加锁和解锁

注意:Lock是接口,不能直接实例化,可以采用其他实现类ReentrantLock构建Lock锁对象

2.常见方法

获得锁:

释放锁:

注意:

        1.创建的锁对象要用final来修饰

        2.解锁逻辑要放在finally处

6.生产者和消费者

a.概述

生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。

b.等待唤醒机制

Object类常用方法:

案例:

测试类:

public class Main {public static void main(String[] args) {//创建吃货对象Cooker cooker = new Cooker();//创建厨师对象Foodie foodie = new Foodie();//开启多线程cooker.start();foodie.start();}
}

Desk类: 

public class Desk {//定义一个变量标记桌子上是否有食物//0:表示没有食物//1:表示存在食物public static int containFood=0;//表示多线程一共执行多少个回合public static int count=10;//创建一个锁对象public static Object lock=new Object();}

Cooker类: 

public class Cooker extends Thread{@Overridepublic void run() {/*1.循环2.同步代码块3.判断执行回合数是否为04.执行回合数不为0,执行核心代码*/while (true){synchronized (Desk.lock){//回合数为0,结束线程if (Desk.count==0){break;}else {//判断是否有食物//没有食物if (Desk.containFood==0){System.out.println("厨师做了一份食物!");Desk.containFood=1;Desk.lock.notifyAll();}else{try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}}}}}
}

Foodie类: 

public class Foodie extends Thread{@Overridepublic void run() {/*1.循环2.同步代码块3.判断执行回合数是否为04.执行回合数不为0,执行核心代码*/while (true){synchronized (Desk.lock){if(Desk.count==0){break;}else {//判断桌子上是否有食物(有食物:开吃  没有食物:提醒厨师开做)if (Desk.containFood==1){//回合数减1Desk.count--;//吃了一份食物System.out.println("吃货吃了一份食物"+"还可以吃"+Desk.count+"份食物");//桌子上没有食物Desk.containFood=0;//唤醒厨师Desk.lock.notify();}else {try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}}}}}
}

c.阻塞队列实现等待唤醒机制

阻塞队列ArrayBlockingQueue常见方法:

案例:

测试类:

import java.util.concurrent.ArrayBlockingQueue;
class Main{public static void main(String[] args) {//创建阻塞队列ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);//实例化吃货对象Foodie foodie = new Foodie(queue);//实例化厨师对象Cook cook = new Cook(queue);//开启线程foodie.start();cook.start();}
}

Foodie类: 

import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie() {}public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){try {String take = queue.take();//System.out.println(take);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

Cook类: 

import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread{ArrayBlockingQueue<String> queue;public Cook() {}public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){try {queue.put("面条");//System.out.println("厨师做好了一份面条");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

7.线程池

a.概述

创建一个池子,池子中是空的,提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给线程池下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可,但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

b.利用工具类创建线程池

步骤:

1.创建线程池

2.提交任务

3.所有的任务全部执行完毕,关闭线程池

Executors工具类:

创建一个没有上限的线程池:

创建有上限的线程池:

提交任务:

销毁线程池:

注意:在实际开发过程中,因为服务器24小时不停止,所以线程池一般不会关闭

c.自定义线程池

1.构造方法

注意:

       参数一:核心线程数量          不能小于0

       参数二:最大线程数               不能小于0且最大数量>=核心线程数量

       参数三:空闲线程最大重活时间              不能小于0

       参数四:时间单位                  用TimeUnit指定

       参数五:任务队列                   不能为null

       参数六:创建线程工厂             不能为null

       参数七:任务的拒绝策略               不能为null

任务拒绝策略:

2.底层原理

1.当核心线程满时,再提交任务就会排队

2.当核心线程满时,队伍满时,会创建临时线程

3.当核心线程满时,队伍满时,临时线程满时,会触发任务拒绝策列

3.线程池中线程数量

        


http://www.mrgr.cn/news/62340.html

相关文章:

  • pip在ubuntu下换源
  • 计算机网络-常用网络命令和工具
  • React第十二章(useSyncExternalStore)
  • Java 实现协同过滤算法推荐算法
  • linux之awk
  • 坚持使用kimi搭建小程序2小时(04天/05天)
  • JAVA程序导致cpu标高排查
  • 微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)
  • 基于java+SpringBoot+Vue的网上租贸系统设计与实现
  • Java8中CompletableFuture.allOf的使用
  • Python飞舞蝙蝠
  • 迪杰斯特拉算法(Dijkstra‘s Algorithm
  • Vue学习记录之二十七 Pinia的使用
  • 97、Python并发编程:多线程实现的两种方式
  • 串口屏控制的自动滑轨
  • 【MySQL】 运维篇—安全管理:数据加密与SSL配置
  • Java基础2-数组
  • Python | Leetcode Python题解之第521题最长特殊序列I
  • C语言 | Leetcode C语言题解之第522题最长特殊序列II
  • C++ | Leetcode C++题解之第522题最长特殊序列II
  • 【多线程场景下事务失效问题如何处理?】
  • 从openjdk17 C++源码角度看 java类成员变量是怎么赋值的
  • 理解环境变量与Shell编程:Linux开发的基础
  • DS18B20+测量系统可编程分辨率高精度数字温度传感器芯片
  • Python——石头剪刀布(附源码+多模式二改优化版)
  • C++学习笔记----9、发现继承的技巧(六)---- 有趣且令人迷惑的继承问题(6)