day16_java多线程(入门了解)

多线程入门

一、线程和进程

  1. 进程

    进程:是指一个内存中运行的应用程序每个进程都有一个独立的内存空间和系统资源,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。进程是系统进行资源分配和调度的独立单位。

    单cpu同一时间点只能执行一件事情,CPU高效的切换让我们觉得是同时进行的

    我们在同一个进程内可以执行多个任务,每个任务就可以看成一个线程

    进程就是正在运行的程序
    进程是系统进行资源分配和调度的独立单位,每一个进程都有它自己的内存空间和系统资源。

    案例:

    百度云盘(一个应用程序:进程)

    下载功能(可以同时下载多个文件)

  2. 线程

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

    在同一个进程内可以执行多个任务,而这个每个任务就是看成线程
    **线程,**是程序执行的单元,执行路径,是程序使用cpu最基本单位。

    cpu 只有一个, 每次只能执行一个(线程)

    一个进程有多个线程

    单线程 :如果程序只有只有一条执行路径 多线程:如果程序有多条执行路径

二、并行与并发

  1. 并发:逻辑上同时发生,指再某一个时间内同时运行的程序
  2. 并行:物理上同时发生,指在某一个时间点同时运行的程序

在这里插入图片描述

三、多线程的意义嘛?

多线程存在的意义:不是提高程序的执行速率,其实是为了提高程序的使用率。
程序的执行其实都是在抢cpu的资源,cpu的执行权。
多个线程抢夺到cpu执行权的概率更大 线程抢夺执行权具有随机性

四、java程序的运行原理

由java命令启动jvm,启动jvm相当于启动了一个进程

接着该进程创建主线程(main)去调用main方法

jvm虚拟机的启动是单线程的还是多线程的?多线程

原因是垃圾回收线程也要启动,不然很容易就内存溢出

五、Thread的基本使用

1、创建线程的步骤
  • 自定义一个类MyThread 继承Thread类
  • 重写run方法
  • 创建一个MyThread对象
  • MyThread对象.start()

注: run 与 start 的区别

  • 直接调用run方法,还是在main线程中执行
  • start() 方法, jvm会创建一个新线程,然后jvm会自动运行新线程的run方法
2、创建多个线程的方法

注:new 多个MyThread对象即可。不是要理解成调用多次start了

MyThread myThread = new MyThread("张三"); //这就是一个线程 
MyThread myThread2 = new MyThread("李四");
myThread.start();
myThread2.start();
3、获得线程的名字
  • String getName()
    返回该线程的名称。

    可以在Thread的子类中直接使用

  • Thread.currentThread() : 得到当前线程对象

System.out.println(Thread.currentThread().getName());
4、多线程独立栈空间

在这里插入图片描述

5、多线程的打印具有随机性

在这里插入图片描述

6、线程的调度

**分时调度:**所有线程轮流使用CPU 的使用权,平均分配每个线程占用 CPU 的时间。

**抢占式调度:**优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),

Java使用的是抢占式调度。

7、设置线程的优先级:

抢占式调度详解:大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我 们上课一边使用idea编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是 在同时运行,”感觉这些软件好像在同一时刻运行着“。 实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

在这里插入图片描述

线程优化级高,并不能让线程先执行。它还是随机的,只是概率高点

六、多线程好处:

  1. 充分利用CPU的资源(多进程), 多线程(为当前程序抢占CPU使用权)

  2. 简化编程模型

  3. 带来良好的用户体验

  4. 多个线程之间互不干扰

七、线程的控制

1、Thread类API

属性:

NORM_PRIORITY : 值为 5
MAX_PRIORITY : 值为 10
MIN_PRIORITY : 值为 1

构造方法:

  • Thread():分配一个新的 Thread对象。。

  • Thread(String name):分配一个指定名字的新的线程对象。

  • Thread(Runnable target):分配一个带有指定目标新的线程对象。

  • Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

常用方法:

  • String getName():获取当前线程名称。
  • static Thread currentThread():返回对当前正在执行的线程对象的引用。
  • void setName(String name):将此线程的名称更改为等于参数 name 。
  • void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • void run():此线程要执行的任务在此处定义代码。
  • static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • int getPriority() :返回此线程的优先级。
  • void setPriority(int newPriority) :更改此线程的优先级。
  • void join() :等待这个线程死亡。
  • static void yield():对调度程序的一个暗示,即当前线程愿意让出当前使用的处理器。
  • void interrupt():中断这个线程。
  • boolean isAlive():测试这个线程是否活着。
  • Thread.State getState():返回此线程的状态。
2、线程的休眠
public void run() {
    // 放让线程执行代码块
    for(int i=0;i<10;i++){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(getName()+"我是最棒的:"+i);
    }
}
3、线程的加入

void join() :等待这个线程死亡。

MyThread myThread = new MyThread("张三"); //这就是一个线程
MyThread myThread2 = new MyThread("李四");
myThread.start();
try {
    myThread.join();
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}
myThread2.start();
4、线程的礼让

static void yield():对调度程序的一个暗示,即当前线程愿意让出当前使用的处理器。

public void run() {
// 放让线程执行代码块
for(int i=0;i<10;i++){
Thread.yield(); //让出cpu 使用权
System.out.println(getName()+"我是最棒的:"+i);
}
}
5、线程的中断
  • public final void stop (): 让线程停止,过时了,但是还可以使用。
  • **public void interrupt ( )**∶中断线程。把线程的状态终止,并抛出一个InterruptedException.
6、线程的守护

public final void setDaemon(boolean on)

将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。

线程启动前必须调用此方法。

MyThread myThread = new MyThread("张三"); //这就是一个线程
MyThread myThread2 = new MyThread("李四");
myThread.setDaemon(true);
myThread2.setDaemon(true);
myThread.start();
myThread2.start();
//myThread.interrupt();

for(int i=0;i<10;i++){

    System.out.println(Thread.currentThread().getName()+"我是最棒的:"+i);
}

八、Runnable创建多线程

runable方式创建线程
  1. 创建一个Runable接口的实现类MyRunable
  2. 重写run方法
  3. 创建实现类MyRunable对象, myrunable
  4. 创建Thread类的对象,把 myrunable对象作为构造参数传过去

九、创建多线程方式总结

实现多线程方式:两种

方法1:Thread类

  1. 自定义一个MyThread类继承Thread类
  2. 在MyThread类中重写run方法
  3. 创建MyThread类的对象
  4. 启动线程对象。start()方法

方法2:Runable 接口

  1. 定义一个Runable接口的实现类,MyRunable类
  2. 在MyRunable类中重写run方法
  3. 创建MyRunable类的对象myRunable
  4. 创建Thread类的对象,且将myRunable对象作构造方法的参数传递
  5. 启动线程 . start方法

问题:为什么有了方法1,还需要方法2

  1. 为了避免 java 中由于单继承带来的局限性
  2. runable接口实现的线程,适合多个相同的代码去处理同一个资源的情况,把线程的同程序的代码,数据进行有效分享(资源共享),体现了面向对象思想

线程安全问题

案例:卖票????

出现同票原因

// 分析同票的原因
// 线程抢夺cpu 执行权时,执行的代码具有原子性
// 原子性是指最基本的代码(最小的语句)

出现负票的原因???

如何解决

什么情况会出现线程安全问题

  1. 是否多线程环境
  2. 是否有共享资源
  3. 是否有多条语句操作共享资源

同步锁

同步代码块

锁对象:任意对象

语法:

synchronized (obj){
    //操作共享数据的代码

}
同步方法

同步方法的锁对象是this

public synchronized void sellTicket(){
    if(ticket>0){ // c1  c2  c3  ticket = 1
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"第"+ticket+"张票");
        ticket--;// 0
    }
}
同步静态方法

同步方法的锁对象是当前类的字节码对象(反射会学习)

public synchronized  static void sellTicket(){
    if(ticket>0){ // c1  c2  c3  ticket = 1
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"第"+ticket+"张票");
        ticket--;// 0
    }
}
Lock锁
public void run() {
        while (true){
            lock.lock(); //上锁
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"第"+ticket+"张票");
                ticket--;// 0
            }
            lock.unlock(); //释放锁
        }
    }
synchronized与lock的区别
  • synchronized:jvm级别的锁,jvm自动上锁和解锁
  • lock锁:java代码的锁,需要手动的加锁和释放锁
死锁

多线程产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不妨。
  • 不可剥夺调用:进程已获得资源,在未使用完成前,不能被剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
public class MyThread extends Thread{
    private boolean flag;
    public MyThread(Boolean flag){
        this.flag = flag;
    }
    @Override
    public void run() {
       if(flag){
           synchronized (LockObject.objA) { //
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               System.out.println("我有锁A,需要锁B");
               synchronized (LockObject.objB) {
                   System.out.println(Thread.currentThread().getName() + ":我有锁B");
               }
           }
       }else{
           synchronized (LockObject.objB) { //
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               System.out.println("我有锁A,需要锁B");
               synchronized (LockObject.objA) {
                   System.out.println(Thread.currentThread().getName() + ":我有锁B");
               }
           }
       }
    }
}
public class LockObject {
    public static Object objA = new Object();
    public static Object objB = new Object();
}
public class Example02 {
    public static void main(String[] args) {

        MyThread thread = new MyThread(true);
        MyThread thread2 = new MyThread(false);
        thread.start();
        thread2.start();
    }
}

十、线程的生命周期

线程的生命周期(状态):

  • 新建:创建一个线程对象
  • 就绪:对象调用start()方法,有执行资格,但是没有抢到CPU资源
  • 运行:抢到了CPU资源,有执行权
  • 阻塞:通过sleep(),wait()等方法让线程中断运行,没有了执行资格
  • 死亡:线程执行完毕,等待垃圾回收

在这里插入图片描述

十一、线程之间的通信

生产者:

​ 先查看是否有资源,有就等待,没有就生产,生产后通知消费者来消费资源

消费者:

​ 先查看是否有资源,有就消费资源,没有就通知生产者生产资源并等待

在这里插入图片描述

public class BaoZi { // 公共资源包子类
    public String name;
    public boolean flag=false; //是否有资源
    public BaoZi(){

    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public BaoZi(String name){
        this.name = name;
    }
}

// 生产者,包包子的
public class SetBaoZi extends Thread{ 
    private BaoZi bz;
    private int x = 0;
    public SetBaoZi(BaoZi bz){
        this.bz = bz;
    }
    @Override
    public void run() {
        while (true){
            synchronized (bz){
                if (bz.flag){ // 有资源,不需要包包子,等待资源消耗完
                    try{
                        bz.wait();
                    } catch (InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }
                if (x%2==0){
                    bz.name = "小笼包";
                }else {
                    bz.name = "酱肉包";
                }
                bz.notify(); // 唤醒消费者线程
                bz.flag = true;
                x++;
            }
        }
    }
}

// 消费者
public class GetBaoZi extends Thread{
    private BaoZi bz;
    public GetBaoZi(BaoZi bz) {
        this.bz = bz;
    }
    @Override
    public void run() {
        while (true){
            synchronized (bz){
                if (!bz.flag){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.println("消费了包子:"+bz.name);
                bz.flag = false;
                bz.notify(); // 包子被消费,唤醒生产者线程包包子
            }
        }
    }
}

// 测试
public class Test {
    public static void main(String[] args) {
        BaoZi baoZi = new BaoZi();
        SetBaoZi setBaoZi = new SetBaoZi(baoZi);
        GetBaoZi getBaoZi = new GetBaoZi(baoZi);
        setBaoZi.start();
        getBaoZi.start();
    }
}

// 加锁,解决了消费资源出错的问题
// 使用wait和notify解决了多次消费同一资源的问题

线程间通信的内存图

常见情况:

  1. 新建 – 就绪 – 运行 – 死亡
  2. 新建 – 就绪 – 运行 – 就绪 – 运行 – 死亡
  3. 新建 – 就绪 – 运行 – 等待阻塞 – 同步阻塞 – 就绪 – 运行–死亡
  4. 新建 – 就绪 – 运行 – 其它阻塞 – 就绪 – 运行–死亡
  5. 新建 – 就绪 – 运行 – 同步阻塞 – 就绪 – 运行–死亡

在这里插入图片描述

十二、线程池

使用线程池的思想,池化思想可以提高重用性和效率(Executors)。

class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("我是callable方法");
        return null;
    }
}
public class Example01 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        MyCallable myCallable1 = new MyCallable();
        MyCallable myCallable2 = new MyCallable();
        service.submit(myCallable1);
        service.submit(myCallable2);
    }
}

十二、线程池

使用线程池的思想,池化思想可以提高重用性和效率(Executors)。

class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("我是callable方法");
        return null;
    }
}
public class Example01 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        MyCallable myCallable1 = new MyCallable();
        MyCallable myCallable2 = new MyCallable();
        service.submit(myCallable1);
        service.submit(myCallable2);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从这三个方面,可以快速分析光伏系统设计方案的可行性!

随着光伏技术的不断发展&#xff0c;光伏项目也越来越受欢迎。光伏发电是利用半导体界面的光生伏特效应而将光能直接转变为电能的一种技术。如何分析光伏系统设计方案的可行性&#xff1f; 1.经济可行性分析 需要考虑光伏系统的投资成本&#xff0c;包括太阳能电池板、逆变器…

Qt/QML编程学习之心得:工程中的文件(十二)

Qt生成了工程之后,尤其在QtCreator产生对应的project项目之后,就如同VisualStudio一样,会产生相关的工程文件,那么这些工程文件都是做什么的呢?这里介绍一下。比如产生了一个Qt Widget application,当然如果Qt Quick Application工程会有所不同。 一、.pro和.pro.user …

Java学习总结

1. Java集合体系框架 java.util中包含 Java 最常用的the collections framework。 Java集合类主要由两个根接口Collection和Map派生出来的。 Collection 接口派生出了三个子接口List、Set、Queue。Map 接口 因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。 …

数据库常用锁

锁是计算机在执行多线程或线程时用于并发访问同一共享资源时的同步机制&#xff0c;MySQL中的锁是在服务器层或者存储引擎层实现的&#xff0c;保证了数据访问的一致性与有效性。 MySQL锁可以按模式分类为&#xff1a;乐观锁与悲观锁。 按粒度分可以分为全局锁、表级锁、页级…

计算整数各位数字之和 C语言xdoj29

时间限制: 1 S 内存限制: 1000 Kb 问题描述: 假设n是一个由最多9位数字&#xff08;d9, …, d1&#xff09;组成的正整数。编写一个程序计算n的每一位数字之和 输入说明: 输入数据为一个正整数n 输出说明: 对整数n输出它的各位数字之和后换行 输入样例: …

Android渲染-AHardwareBuffer

本文主要从应用的角度介绍android的native层AHardwareBuffer创建纹理以及保存渲染数据。 HardwareBuffer 要介绍native层的AHardwareBuffer&#xff0c;就需要先从Java层的HardwareBuffer说起。Android官方对于HardwareBuffer介绍如下&#xff1a; HardwareBuffer wraps a na…

【lesson11】数据类型之string类型

文章目录 数据类型分类string类型set类型测试 enum类型测试 string类型的内容查找找所有女生&#xff08;enum中&#xff09;找爱好有游泳的人&#xff08;set中&#xff09;找到爱好中有足球和篮球的人 数据类型分类 string类型 set类型 说明&#xff1a; set&#xff1a;集…

uniCloud(一) 新建项目、初始化服务空间、云对象访问测试

一、新建一个带有unicloud 二、创建一个服务空间 1. 右键uniCloud&#xff0c;关联云服务空间 我当前没有服务空间&#xff0c;需要新建一个服务空间&#xff0c;之后将其关联。初始化服务空间需要的时间有点长 服务空间初始化成功后&#xff0c;刷新HBuilder&#xff0c;勾选…

GitHub 开源开发者日,沙龙见闻与洞察

前言 12月10日&#xff0c;我有幸受邀参加了 GitHub Universe 2023 Watch Party in Shanghai – 开源开发者日。 这次活动在上海微软 Reactor 线下举行&#xff0c;与数位 AI 及开源大咖汇聚现场&#xff0c;与开源爱好者们共同畅聊开源之旅。 活动介绍 整个线下大会中&#…

OpenCV-python numpy使用和基本作图

文章目录 一、实验目的二、实验内容三、实验过程Numpy1.NumPy 操作2.NumPy Ndarray 对象3.NumPy 基本类型4.NumPy 数组属性ndarray.ndimndarray.shapendarray.itemsizendarray.flags 5.NumPy 创建数组numpy.emptynumpy.zerosnumpy.ones 6.NumPy 从已有的数组创建数组numpy.asar…

openGauss学习笔记-152 openGauss 数据库运维-备份与恢复-物理备份与恢复之PITR恢复

文章目录 openGauss学习笔记-152 openGauss 数据库运维-备份与恢复-物理备份与恢复之PITR恢复152.1 背景信息152.2 前提条件152.3 PITR恢复流程152.4 recovery.conf文件配置**152.4.1 归档恢复配置****152.4.2 恢复目标设置** openGauss学习笔记-152 openGauss 数据库运维-备份…

微信小程序 长按录音+录制视频

<view class"bigCircle" bindtouchstart"start" bindtouchend"stop"><view class"smallCircle {{startVedio?onVedio:}}"><text>{{startVedio?正在录音:长按录音}}</text></view> </view> <…

如何使用Imagewheel本地搭建一个简单的的私人图床公网可访问?

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

前后端请求之nginx配置

问题&#xff1a; 前端发送的请求&#xff0c;是如何请求到后端服务器的&#xff1f; 如&#xff0c;前端请求地址&#xff1a;http://loclhost/api/employee/login&#xff1a; 后端相应请求地址&#xff1a;http://loclhost:8080/admin/employee/login 回答&#xff1a; …

vue 限制在指定容器内可拖拽的div

<template><div class"container" id"container"><div class"drag-box center" v-drag v-if"isShowDrag"><div>无法拖拽出容器的div浮窗</div></div></div> </template><script&g…

奇点云2023数智科技大会来了,“双12”直播见!

企业数字化进程深入的同时&#xff0c;也在越来越多的新问题中“越陷越深”&#xff1a; 数据暴涨&#xff0c;作业量和分析维度不同以往&#xff0c;即便加了机器&#xff0c;仍然一查就崩&#xff1b; 终于搞定新增渠道数据的OneID融合&#xff0c;又出现几个渠道要变更&…

二十、FreeRTOS之Tickless低功耗模式

本节需要掌握以下内容&#xff1a; 1&#xff0c;低功耗模式简介&#xff08;了解&#xff09; 2&#xff0c; Tickless模式详解&#xff08;熟悉&#xff09; 3&#xff0c; Tickless模式相关配置项&#xff08;掌握&#xff09; 4&#xff0c;Tickless低功耗模式实验&…

佳明(Garmin) fēnix 7X 增加小睡检测功能

文章目录 &#xff08;一&#xff09;零星小睡&#xff08;二&#xff09;小睡检测&#xff08;三&#xff09;吐槽佳明&#xff08;3.1&#xff09;心率检测&#xff08;3.2&#xff09;光线感应器&#xff08;3.3&#xff09;手表重量&#xff08;3.4&#xff09;手表续航 &a…

适配滑动宽度(Vant)

vueuse/core 介绍&#xff1a; 文档https://vueuse.org/core/useWindowSize/ 是一个基于 组合API 封装的库&#xff0c;提供了一些网站开发常用的工具函数&#xff0c;切得到的是响应式数据 例如&#xff1a; 在 375 宽度设备&#xff0c;滚动宽度为 150 在其他设备需要等比例…

扔掉xshell,基于 QT 实现一个串口命令行工具(带源码)

背景 xshell 带有支持串口的命令行能力&#xff0c; 可以方便的和下位机用命令进行交互&#xff0c;如下图所示&#xff1a; msh > msh > msh >version\ | / - RT - Thread Operating System/ | \ 3.1.3 build Nov 7 20232006 - 2019 Copyright by rt-thre…