7月25日JavaSE学习笔记

news2024/11/17 14:42:56

线程的生命周期中,等待是主动的,阻塞是被动的

锁对象

创建锁对象,锁对象同一时间只允许一个线程进入

    //创建锁对象
    Lock lock=new ReentrantLock(true);//创建可重入锁

可重入锁:在嵌套代码块中,锁对象一样就可以直接进入执行

公平锁:保证线程获取锁的顺序与线程请求锁的顺序一致,即按照先来先服务的原则。

非公平锁:线程获取锁的顺序与线程请求锁的顺序无关,允许插队操作。

默认情况下的锁都是非公平锁,传入一个参数为true即为公平锁


加锁 lock

解锁 unlock

有加锁就要有解锁,否则会造成死锁

尝试加锁 tryLock

尝试加锁,如果加锁成功会返回true,失败返回false

    public void method(){
        //lock.lock();//加锁
        if (lock.tryLock()){//尝试加锁:加锁成功返回true,失败false
            System.out.println(Thread.currentThread().getName()+"加锁成功");
            System.out.println(Thread.currentThread().getName()+"进入方法");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"结束方法");
            lock.unlock();//解锁,忘记会发生死锁状态
        }else {
            System.out.println(Thread.currentThread().getName()+"加锁未成功");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            method();
        }

死锁(Deadlock)指的是在多线程编程中,两个或多个线程互相持有对方所需的资源,并且由于无法获得所需的资源而陷入无限等待的状态,导致程序无法继续执行下去。

发生死锁的必要条件有四个,分别是:

  1. 互斥条件(Mutual Exclusion):至少有一个资源每次只能被一个线程占用。

  2. 请求与保持条件(Hold and Wait):线程在持有资源的同时还可以请求其他资源。

  3. 不剥夺条件(No Preemption):线程不能被强制剥夺已经获得的资源,只能在自愿释放资源后才能由其他线程获取。

  4. 循环等待条件(Circular Wait):存在一种等待循环,即线程1等待线程2持有的资源,线程2等待线程3持有的资源,...,线程N等待线程1持有的资源。

当以上四个条件同时满足时,就可能发生死锁。一旦发生死锁,线程无法继续执行下去,程序会陷入无法解开的僵局,需要通过外部干预来解决。

为了避免死锁的发生,可以采取以下措施:

  1. 破坏互斥条件:例如,对于某些资源可以采用共享的方式,允许多个线程同时访问

  2. 破坏请求与保持条件:一次性申请所有需要的资源,而不是一个一个地申请。

  3. 破坏不剥夺条件:允许线程在持有资源的同时,根据需要剥夺其他线程的资源

  4. 破坏循环等待条件:通过定义资源的顺序,要求线程按照顺序申请资源,避免循环等待的发生。

以上措施可以在设计和实现多线程程序时考虑,来减少死锁的概率和影响。此外,通过合理的资源管理和使用,以及良好的编码规范,也可以减少死锁的发生。


ReentrantReadWriteLock

public static ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();

ReentrantReadWriteLock不是一个锁,是一个锁容器,里面有一个读锁(共享锁)和一个写锁(独占锁);原本有读锁时可以再加一个读锁,但写锁只能在没有任何线程占用时才能添加。

读锁 readLock

读锁常用于读多写少的场景,例如缓存系统、数据库查询等。多个线程可以同时获取读锁,同时读取共享资源,互不干扰。这样可以提高并发性和读取的效率。

    public int get(int index){
        Lock readLock = rwLock.readLock();
        readLock.lock();
        System.out.println(Thread.currentThread().getName()+"运行Get开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(index >= size){
            throw new IndexOutOfBoundsException("index is"+index);
        }
        System.out.println(Thread.currentThread().getName()+"运行Get结束");
        readLock.unlock();
        return values[index];
    }
写锁 writeLock

写锁常用于写多读少的场景,例如数据更新、写入操作等。在写锁加锁期间,其他线程无法获取读锁或写锁,确保了对共享资源的一致性和完整性。写锁是排他的,只允许一个线程进行写入操作。

    public boolean add(int item){
        Lock writeLock = rwLock.writeLock();
        writeLock.lock();
        System.out.println(Thread.currentThread().getName()+"运行Add开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(size>values.length-1){
            return false;
        }
        values[size++]=item;
        System.out.println(Thread.currentThread().getName()+"运行Add结束");
        writeLock.unlock();
        return true;
    }

线程通讯

线程通讯通过锁对象实现

如何的对象都可以是锁对象,锁对象的方法在Object类中定义,但只有对象是锁对象的时候才能调用锁对象的方法,否则会报状态异常。

wait 等待

让执行到该行代码的线程进入等待状态(等待池)

notify 唤醒

唤醒一条被该锁对象wait的线程

当多个线程调用了wait方法进入等待状态时,一个线程调用notify方法会随机唤醒其中一个等待中的线程。也就是说,调用notify方法会唤醒一个线程,但是不确定唤醒的是哪个线程。

notifyAll 唤醒所有

唤醒所有被该锁对象wait的线程

    public static final Object OBJECT=new Object();

    public static void method(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        synchronized (OBJECT){//指定锁对象
            OBJECT.notify();//唤醒一条被该锁对象wait的线程
            //OBJECT.notifyAll();//唤醒全部被该锁对象wait的线程
            System.out.println(Thread.currentThread().getName()+"进入同步代码块");
            try {
                try {
                    System.out.println(Thread.currentThread().getName()+"进入等待状态");
                    OBJECT.wait();//让执行到该行代码的线程进入等待状态(等待池)
                    //结束等待状态即进入就绪状态
                    System.out.println(Thread.currentThread().getName()+"重新运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"结束同步代码块");
            OBJECT.notify();
        }
    }

wait和sleep的区别?

wait是Object类中定义的方法,可以由锁对象调用,让执行到该行代码的线程进入到等待状态;

sleep是Thread类中定义的静态方法,可以让执行到该行代码的线程进入到等待状态

区别:1.sleep需要传入一个毫秒数,达到时间后会自动唤醒

              wait不能自动唤醒,必须调用notify或notifyAll方法唤醒

           2.sleep方法保持锁状态进入等待状态

              wait方法会解除锁状态,其他线程可以进入运行

当一个线程调用sleep方法时,它会暂停执行一段时间,但是它不会释放锁。换句话说,其他线程仍然无法进入该代码块,因为锁状态被保持。

而当一个线程调用wait方法时,它会释放锁,并进入等待状态。其他线程可以获取该锁并执行相应的代码块。当wait方法的线程被唤醒后,它会重新尝试获取锁,并继续执行代码。


线程池 Executors

完成线程的创建、管理和销毁工作

线程池是一种管理和维护线程的机制,它可以在应用程序中预先创建一组可重复使用的线程,并在需要时分配这些线程来执行任务。

线程池的主要作用是优化线程的创建和销毁开销,提高线程的重用率和执行效率。通过线程池,可以将任务提交给线程池,线程池会自动管理线程的生命周期,使得线程可以被重复利用,避免频繁地创建和销毁线程的开销。

创建线程池对象

线程池ThreadPoolExecutor的构造方法有7个参数依次是:

int corePoolSize                   1.核心线程数
int maximumPoolSize                2.最大线程数
long keepAliveTime                 3.持续存活时间
TimeUnit unit                      4.时间单位
BlockingQueue<Runnable> workQueue  5.任务队列
ThreadFactory threadFactory        6.线程工厂(用于创建线程)
RejectedExecutionHandler handler   7.拒绝执行的处理器(回绝策略)
        ArrayBlockingQueue qu = new ArrayBlockingQueue(12);
        //创建线程池对象
        ThreadPoolExecutor tpe=new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, 
                                qu, Executors.defaultThreadFactory(), 
                                new ThreadPoolExecutor.AbortPolicy());

实现Runnable接口或者Callable接口都可以作为线程任务执行

 execute 方法 执行 :

Runnable的线程任务交给线程池执行

        Runnable run =EasyExecutors::method;
        //交给线程池执行
        tpe.execute(run);

 submit 方法 提交:

将实现Runnable接口或Callable接口的线程任务提交给线程池执行

        Callable<String> call = EasyExecutors::methodCall;
        Future<String> f = tpe.submit(call);//提交
        //tpe.submit(run);

shutdown 方法 关闭:

        //关闭线程池
        tpe.shutdown();//结束,销毁

Callable接口

Callable接口定义了一个单一的方法call(),该方法在执行任务时可以返回结果。与Runnable的run()方法不同,call()方法可以返回一个泛型类型的结果,也可以抛出一个Exception。在执行Callable任务时,可以通过Future对象来接收任务的返回结果。

Future

Future类是用来表示异步计算结果的,它提供了一系列的方法来获取、取消和监控异步任务的执行状态,使得多线程编程更加方便和灵活。

get 方法 获取 :会等待线程执行完毕

cancel 方法 取消 :

对于正在执行中的任务,cancel方法的mayInterruptIfRunning参数可设为true,这样可通过中断执行线程来中断任务通过其他途径来中断Executor内的线程是不允许的,因为你不知道该线程正在执行哪个任务;而Future对象是Executor创建的,它会与Executor线程池互相配合来实现其任务中断策略。

        Callable<String> call = EasyExecutors::methodCall;
        Future<String> f = tpe.submit(call);//提交

        System.out.println(f.get());//会等待线程执行完毕

        f.cancel(true);//取消任务执行


线程池的4种回绝策略

1. AbortPolicy (默认)放弃该任务并会抛出一个异常RejectedExecutionException

2. CallerRunsPolicy 调用者执行,让传递任务的线程执行此任务

3. DiscardOldestPolicy 放弃队列中最早提交的任务,不会抛出异常

4. DiscardPolicy 直接放弃新的任务,不会抛出异常


线程池的工作原理:

        任务放置在工作队列中

1. 先检查池中是否有空闲的线程,如果有就让该线程执行任务

2. 如果没有空闲的线程,判断池中的线程数量有没有达到核心线程数:

        没有达到核心线程数,创建新的线程执行任务,直到填满核心线程数;

        如果已经达到,优先在队列中存储,直到队列填满

3. 工作队列填满后,再添加新的任务,看是否达到最大线程数:

        如果没有,创建新的线程执行任务,直到填满最大线程数;

        已经填满最大线程数,队列也已经填满,没有空闲线程,就执行回绝策略

4. 线程池中的线程达到(超过)核心线程数,超出的数量在空闲时会根据存活时间进行销毁

        直到数量达到核心线程数,如果线程的数量少于核心线程数,不会消亡


Java中内置的线程池:

        //Java中内置的线程池

        //可以根据工作任务来创建线程,如果没有空闲的线程就创建新的线程  线程存活时间60s
        Executors.newCachedThreadPool();

        //设定最大线程数量的线程池
        Executors.newFixedThreadPool(10);

        //提供定时运行的处理方案
        Executors.newScheduledThreadPool(10);

        //创建一个具有单个线程的线程池,保障任务队列完全按照顺序执行
        Executors.newSingleThreadExecutor();

在高并发高任务情况下,核心线程数怎么设置合适?

取决于以下几个因素:

  1. 系统资源:核心线程数应根据系统的处理能力来确定。如果系统的CPU、内存等资源充足,可以适当增加核心线程数来提高并发处理能力。

  2. 任务类型:不同类型的任务对线程的需求不同。如果任务是CPU密集型,即需要大量的计算资源,那么线程数可以设置得较少,以避免线程过多而导致资源竞争。如果任务是IO密集型,即需要等待IO操作的完成,那么线程数可以设置得较多,以充分利用CPU资源。

  3. 响应时间:线程池的核心目标是提供快速响应的服务。核心线程数的设置应能够确保任务能够在合理的时间内得到处理,而不是等待线程的创建和启动。

  4. 可用线程数:线程池的总线程数应根据系统的可用线程数来设置。可用线程数是指除了线程池的核心线程数之外,系统还能够分配给线程池的最大线程数。一般情况下,可用线程数可以设置为核心线程数的两倍或更多。

综合考虑以上因素,合适的核心线程数设置可以先根据系统资源和任务类型进行初步估算,然后进行性能测试和调优,根据实际情况进行调整。


枚举类

枚举类是一种特殊的类,它定义了一组常量作为其实例,并提供了一些功能扩展。

在Java中,我们可以使用关键字"enum"来定义枚举类。枚举类的实例通常用于表示一组相关的常量,例如颜色、星期几、季节等。

需要注意的是,枚举类的实例是唯一的,可以通过调用枚举常量的名称进行比较。例如,使用day == Day.MONDAY来比较两个枚举常量是否相等。

关键字 enum 来创建枚举类枚举类首行必须枚举所有实例

枚举类默认继承Enum,但不能写继承自Enum;

是可比较的:根据实例声明的顺序

枚举类是不可序列化的,也不能克隆

public enum EasyColor {
    RED,YELLOW,GREEN,BLUE,PINK;

    public void printColor(){
        System.out.println(this.name());
        System.out.println(this.ordinal());
    }
}
class Test{
    public static void main(String[] args) {
        EasyColor.GREEN.printColor();
    }
}

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

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

相关文章

分享几种电商平台商品数据的批量自动抓取方式

在当今数字化时代&#xff0c;电商平台作为商品交易的重要渠道&#xff0c;其数据对于商家、市场分析师及数据科学家来说具有极高的价值。批量自动抓取电商平台商品数据成为提升业务效率、优化市场策略的重要手段。本文将详细介绍几种主流的电商平台商品数据批量自动抓取方式&a…

PP 三 pp字段含义

单位&#xff1a;生产&#xff0c;销售&#xff0c;采购的单位&#xff0c;和基本单位会存在不一样的情况&#xff0c;所以要进行一个转换 产品组&#xff0c;普通项目类别组&#xff1a;销售来确定 跨工厂物料状态&#xff1a;如果在基本数据1里面&#xff0c;则是跨集团的&…

Kafka知识总结(分区机制+压缩机制+拦截器+副本机制)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 分区机制 分区策略 分区策略是决定生产者将消息发送到哪个分区的…

WPF---Prism视图传参

Prism视图传参方式。 实际应用场景 点击tabitem中的列表数据&#xff0c;同步更新到ListStatic Region对应的界面。目前用两种方式实现了传参数据同步。 第一&#xff0c;事件聚合器&#xff08;EventAggregator&#xff09; 1. 定义事件 创建一个事件类&#xff0c;用于传…

05 循环神经网络

目录 1. 基本概念 2. 简单循环网络 2.1 简单循环网络 2.2 长程依赖问题 3. 循环神经网络的模式与参数学习 3.1 循环神经网络的模式 3.2 参数学习 4. 基于门控的循环神经网络 4.1 长短期记忆网络 4.2 LSTM网络的变体网络 4.3 门控循环单元网络 5. 深层循环神经网络…

算法第十五天:leetcode19.删除链表的倒数第N个节点

一、删除链表的倒数第N个节点的题目描述与链接 19.删除链表的倒数第N个节点的链接如下表所示&#xff0c;您可直接复制下面网址进入力扣学习&#xff0c;在观看下面的内容之前您一定要先做一遍哦&#xff0c;以便让我印象更深刻&#xff01;&#xff01;!https://leetcode.cn/p…

数据结构和算法入门

1.了解数据结构和算法 1.1 二分查找 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半&#xff0c;然后比较目标值与中间元素的大小关系&#xff0c;从而确定应该在左半部分还是右半部分继续查找。这个…

电离层——科普

电离层的发现 图1 电离层区域示意图 在地球上空大约60km至1000km范围内有一个特殊的区域。因为它的存在,使无线电通信成为现实,同时它又是GPS定位的捣乱鬼,它就是电离层。 电离层的发现 1901年,扎营守候在加拿大信号山的意大利科学家马可尼用风筝价高接收天线,接收到了从英格…

【Android】碎片—动态添加、创建Fragment生命周期、通信

简单用法 在一个活动中添加两个碎片&#xff0c;并让这两个碎片平分活动空间 先新建一个左侧碎片布局和一个右侧碎片布局 左侧碎片 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/…

智慧工地视频汇聚管理平台:打造现代化工程管理的全新视界

一、方案背景 科技高速发展的今天&#xff0c;工地施工已发生翻天覆地的变化&#xff0c;传统工地管理模式很容易造成工地管理混乱、安全事故、数据延迟等问题&#xff0c;人力资源的不足也进一步加剧了监管不到位的局面&#xff0c;严重影响了施工进度质量和安全。 视频监控…

LLM及GPT知识点

工欲善其事必先利其器&#xff0c;在了解大语言模型和GPT之前先要了解基本概念。 LLM Large Language Model (LLM) 即大型语言模型&#xff0c;也叫大语言模型&#xff0c;是一种基于深度学习的自然语言处理&#xff08;NLP&#xff09;模型&#xff0c;它能够学习自然语言的语…

【Django】django模板与前端技术(html模板)

文章目录 “python包html”还是“html包python”?1.新建模板2.模板语法3.views.py测试 “python包html”还是“html包python”? 在前端页面中html代码比python多得多&#xff0c;所以一定是html包python最优&#xff01;于是引出今天的模板。 大体分为三个步骤&#xff1a;…

【Python面试题收录】Python编程基础练习题②(数据类型+文件操作+时间操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题 编写一个函数&#xff0c;实现&#xff1a;先去除左右空白符&#xff0c;自动检测输入的数据类型&#xff0c;如果是整数就转换成二进制形式并返回出结果&#xff1b…

什么是数据标注?

什么是数据标注&#xff1f; 数据标注是在原始数据上添加结构化信息的过程&#xff0c;这些信息通常以标签或元数据的形式存在&#xff0c;目的是让机器能够理解和“学习”数据的特征&#xff0c;从而提高算法的准确性和效率。 数据标注是机器学习和人工智能开发中不可或缺的一…

网络地址转换技术

一、实验日期与地址 1、实验日期&#xff1a;2024年xx月xx日 2、实验地址&#xff1a;xxx 二、实验目的 1、理解源NAT应用场景及原理&#xff1b; 2、掌握NAT Server的配置方法&#xff1b; 3、掌握NAT双出口的配置方法&#xff1b; 4、掌握域内NAT的配置方法。 三、实…

【C++】标准库类型vector

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C ⚙️操作环境:Visual Studio 2022 目录 vector对象集合简介 vector对象集合常用接口(成员函数) &#x1f4cc;vector对象集合模板默认成员函数 &#x1f38f;vector对象集合模板构造函数 &#x1f38f;vector对象…

【vue3|第18期】Vue-Router路由的三种传参方式

日期:2024年7月17日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.408…

HarmonyOS入门-状态管理

View(UI)&#xff1a;UI渲染&#xff0c;指将build方法内的UI描述和Builder装饰的方法内的UI描述映射到界面。 State&#xff1a;状态&#xff0c;指驱动UI更新的数据。用户通过触发组件的事件方法&#xff0c;改变状态数据。状态数据的改变&#xff0c;引起UI的重新渲染。 装…

<PLC><HMI><汇川>在汇川HMI画面中,如何为UI设置全局样式?

前言 汇川的HMI软件是使用了Qt来编写的,因此在汇川的HMI程序编写过程,是支持使用qt的样式来自定义部件样式的,即qss格式。 概述 汇川的软件本身提供三个系统的style样式,我们可以直接使用,但是,如果系统提供的样式不符合你的需求,那么你可以对其进行修改,或者自己新建…

Pytorch使用教学4-张量的索引

1 张量的符号索引 张量也是有序序列&#xff0c;我们可以根据每个元素在系统内的顺序位置&#xff0c;来找出特定的元素&#xff0c;也就是索引。 1.1 一维张量的索引 一维张量由零维张量构成 一维张量索引与Python中的索引一样是是从左到右&#xff0c;从0开始的&#xff…