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

java多线程与JUC

进程线程

进程:进程是操作系统分配资源的基本单位。在电脑中,一个软件就是一个进程

线程:线程是CPU调度的基本单位,是进程内的执行单元。相当于一个软件中的不同功能

多线程程序的特点:程序可以同时去做多件事,CPU可以在多件事之间切换,把等待的空余时间充分利用起来,提高程序的运行效率

多线程的应用场景?

只要你想让多个事情同时运行就需要用到多线程。

比如:软件中的耗时、等待操作、所有的聊天软件、所有的服务器。

并发并行

并发:在一个CPU交替执行

并行:在多个CPU同时执行

电脑CPU线程数就表示同一时间可以同时运行的线程数量

4线程,表示可以同时并行4个线程,当时要运行的线程数量过多时,就会并发和并行同时发生

多线程实现方式

1,继承Thread类

  • 先自己定义一个类进程Thread类
  • 重写run方法,编写线程要执行的代码
  • 创建子类对象,调用start方法,启动线程

2,实现Runnable接口

  • 创建一个自己类实现Runnable接口
  • 重写run方法,编写线程要执行的代码
  • 创建自己类的对象
  • 创建一个Thread类的对象并传递自己类的对象,并开启线程

3,实现Callable接口或者Future接口(有返回值)

对前面两种的优化,可以获取到多线程运行的结果。

  • 创建一个类MyCallable(自己随便创建的类)实现callable接口
  • 重写call方法(是有返回值的,表示多线程运行的结果)
  • 以下在测试类中写
  • 创建MyCallable的对象(表示多线程要执行的任务)
  • 创建FutureTask的对象(将MyCallable对象传入。作用管理多线程运行的结果,Future是一个接口)
  • 创建Thread类的对象,并启动(将FutureTask对象传入。表示线程)

三种实现方式对比


Thread中常见的成员方法

细节:

  1. 如果我们没有给线程设置名字,线程也是有默认的名字(格式:Thread-X,X是序号,从0开始)。创建Thread对象时,也可以利用构造方法设置名字
  2. 当JVM虚拟机启动之后,会自动的启动多条线程。其中有一条线程就叫做main线程,他的作用就是去调用main方法,并执行里面的代码。
  3. 哪条线程执行到sleep方法,那么哪条线程就会在这里停留对应的时间。方法的参数:就表示睡眠的时间,单位是毫秒。1 秒=1000毫秒。当时间到了之后,线程会自动的醒来,继续执行下面的其他代码

线程优先级相关方法

线程调度:抢占式调度(随机性,相互抢),非抢占式调度(所有线程轮流执行)

java中是采用了抢占式调度,所以有设置线程优先级的方法。

设置线程的优先级越大,抢到CPU的概率越大,执行的机会就越大。一共分10档(1-10,没有设置默认为5)

守护线程方法

将某个线程设置为守护线程(备胎线程),其他非守护线程(女神线程)执行结束,守护线程陆续结束,而不是立刻结束,守护线程会执行一小会。

出让线程/插入线程(了解即可)

前面线程执行都是随机的,为了让线程执行尽可能雨露均沾就提出了出让/礼让线程

哪个线程执行了yield方法,就表示出让了当前CPU执行权,让另一个线程执行

哪个线程执行了join方法,就表示把该线程插入到当前线程之前,执行完后再执行别的


线程生命周期

注意:一个线程sleep完后,不会立马执行,需要去抢CPU执行权,抢到了才能执行


线程安全问题 

在卖票时,在还没打印出来时,就被别的线程抢走,票的数量加1,再打印出现超卖和没有第1张票

利用同步代码块

操作共享数据的代码锁起来。

特点1:锁默认打开,有一个线程进去了,锁自动关闭

特点2:里面代码全部执行完,线程出来,锁自动打开

锁:锁对象,只要保持唯一就行,加static关键字。

这种方式叫同步代码块

同步代码块的细节:

  1.  synchronized不能写在循坏外面
  2. synchronized(锁对象),其中锁对象一定要是唯一的,不唯一,那就说明不同线程看的锁是不一样的,那锁相当于没有
public class MyThread extends Thread {//表示这个类所有的对象,都共享ticket数据static int ticket = 0;//0 ~ 99@Overridepublic void run() {while (true) {synchronized (MyThread.class) {//同步代码块if (ticket < 100) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在卖第" + ticket + "张票!!!");} else {break;}}}}}
public class ThreadDemo {public static void main(String[] args) {/*需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票*///创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();//起名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//开启线程t1.start();t2.start();t3.start();}
}

利用同步方法

直接把synchronized关键字加到方法上

不知道将哪些操作写到同步方法里。那就先写同步代码块,再将同步代码块抽取成一个方法即可。

写多线程的套路

  1. 先写一个循环
  2. 写synchronized的同步代码块/同步方法/lock锁
  3. 判断共享数据是否到了末尾,先写到了末尾(不可用)的情况,到了末尾写起来更简单
  4. 判断共享数据是否到了末尾,没末尾的情况,写核心逻辑
public class MyRunnable implements Runnable {//不用写static,因为MyRunnable对象是作为一个参数,让线程去执行的,所以MyRunnable只会创建一个MyRunnable对象int ticket = 0;@Overridepublic void run() {//1.循环while (true) {//2.同步代码块(同步方法)if (method()) break;}}//thisprivate synchronized boolean method() {//3.判断共享数据是否到了末尾,如果到了末尾if (ticket == 100) {return true;} else {//4.判断共享数据是否到了末尾,如果没有到末尾try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");}return false;}
}
public class ThreadDemo {public static void main(String[] args) {/*需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票利用同步方法完成技巧:同步代码块*/MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}

StringBuffer:多线程下是安全的,里面每一个方法都加了synchronized锁

StringBuilder:多线程下是不安全的

synchronized是自动加锁解锁的,有没有办法手动控制加锁解锁呢?

Lock锁

一定要保证多个线程共享同一把锁

保证每个线程执行完后都要释放锁


死锁

死锁是一个错误,以后不要放错。死锁就是锁出现了嵌套。A锁着B要的资源,B锁着A要的资源


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

线程执行是随机顺序,因此提出了生产者和消费者(等待唤醒机制)。可以让生产者生产数据,消费者消费数据,还需要有一个东西来控制线程的执行(核心思想)。

理想情况

生产者抢到CPU,生产数据,将数据放到桌子上,吃货来吃。相当于厨师做一个,吃货吃一个

消费者等待情况

假设消费者先抢到CPU执行权,桌子上没有东西,只有等待wait,等待时,CPU执行权被厨师抢到,厨师看一看桌子上有没有面条,没有就开始做面条,做完放到桌子上,此时吃货还在wait,厨师就要唤醒notify吃货来吃。

生产者等待情况

生产者先抢到CPU执行权,看桌子上有没有面条,没有就做面条,放到桌子上,喊人来吃,结果还是生产者抢到CPU执行权,此时生产者做不了面条,所以生产者只能等待wait,CPU执行权就会被消费者抢走

涉及到的方法

wait:一直等待,等到有人唤醒

notify方法:如果等待的线程较多,只能随机唤醒其中一个

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

好比是连接生产者和消费之间是一个管道,厨师做完面放到管道里,消费者直接从管道里取面条吃,我们可以规定管道里最多能放的面条数量,这个管道就是阻塞队列。

阻塞:put数据时:放不进去,会等着,也叫做阻塞。take数据时:取出第一个数据,取不到会等着,也叫做阻塞。

阻塞队列的继承结构:

创建阻塞队列的对象:ArrayBlockingQueue/LinkedBlockingQueue

put/take方法底层自动加了锁,不用再加锁了

线程的状态

JVM中实际上是没有运行状态的,为了方便理解加上的。

API帮助文档查询结果: 

 JVM为什么没有运行状态?

当线程抢到CPU执行权,JVM会把当前线程交给操作系统去管理了,所以JVM没有定义


 

线程池

相关文章:

  • ck-editor5的研究 (4):初步使用 CKEditor5 的插件功能
  • Cesium快速入门到精通系列教程三
  • 高速串行接口
  • Spring Boot 4.0实战:构建高并发电商系统
  • ArkTS基础
  • spining-lidar的电机和激光雷达体(lidar-imu)之间的标定
  • VMware-VMRC-12.0.1-18113358安装包下载安装与使用(附下载)
  • 数学分析——一致性(均匀性)和收敛
  • 高速串行通信解惑说明
  • ReLU的变体
  • 【项目记录】登录认证(下)
  • vscode 代理模式(agent mode),简单尝试一下。
  • Day42 Python打卡训练营
  • powershell7.5@.net环境@pwsh7.5在部分windows10系统下的运行问题
  • 机器人学基础——正运动学(理论推导及c++实现)
  • 智能指针unique
  • Launcher3体系化之路
  • day16 leetcode-hot100-30(链表9)
  • 开源版 PyMOL 如何绘制 新冠病毒 分子结构?
  • NX811NX816美光颗粒固态NX840NX845
  • 北京的网站建设公司有哪些/优化关键词排名seo软件
  • 企业做网站推广产品需要多少钱/软文类型
  • 什么网站权威评价搜索引擎优劣/企业互联网推广
  • 郑州市做网站的公司/优化师培训机构
  • 网站源代码 php/如何进行关键词分析
  • 企石网站建设/公众号怎么做文章推广