Java 进阶:多线程2

Java进阶 ——— Java多线程(二)之如何开启多线程 上面罗列了三种启动多线程的方式,包括继承Thread类,实现Runnable接口,使用ThreadFactory线程创建,推荐使用第二种,毕竟组合优于继承。 阅读详情

一、Lock 接口

1. 线程锁

  • 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁
//锁的使用
Lock l = new ReentrantLock(); //创建一个锁对象
l.lock();
try {
    // access the resource protected by this lock
} finally {
    l.unlock();
}
  • Lock比Synchronize更为灵活的功能:
  • boolean tryLock()仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。

2. 同步弊端

  • 影响效率
  • 如果出现了嵌套锁,容易产生死锁

3. 死锁

  • 死锁:死锁是指两个以上的线程在执行过程中,因为争夺资源而产生的一种相互等待的现象

二、线程间的通信

1. wait()

  • 导致当前线程处于等待状态
  • 只有在其他线程线程中调用了notify()方法或 notifyAll()来唤醒,因为 wait()方法阻塞了线程
  • 在调用 wait方法之前,当前线程必须拥有此对象监视器(锁对象),换句话说,必须在 锁对象 上调用 wait方法(此方法只应该由作为此对象监视器的所有者的线程 来调用)
  • 一旦在锁对象上调用了 wait方法,紧接着:
    • 当前线程放弃 cpu 执行权,并等待
    • 放弃持有的 锁对象

wait()和 sleep()比较
相同点:使当前线程放弃cpu执行权,处于阻塞状态
不同点:

  1. 线程因为 sleep() 方法 处于阻塞状态的时候,不会放弃所持有的锁对象;线程因为wait() 处于阻塞状态的时候,会放弃锁对象
  2. 使用条件: sleep() 没有任何特殊条件; 使用 wait() 则必须持有锁,在锁对象上调用**wait()**方法
  3. 唤醒条件:sleep() 的唤醒条件是休眠时间结束;wait() 被唤醒,只能是在其它线程中调用了同一个锁对象的 notify() 或者 **notifyAll()**方法

2. nofity()

  • 唤醒在此对象(调用wait的同一个锁对象) 监视器上等待的单个线程;如果所有线程都在此对象上等待,则会选择唤醒其中一个线程
  • 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程
  • 被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争

3. notifyAll()

  • 唤醒在此对象监视器上等待的所有线程

三、线程池

1. 概述

  • 我们创建一个线程,只能使用一次
  • 线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
  • 线程池的原理:线程池里每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一次被使用

为什么要使用线程池?

  1. 在 java 中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。
  2. 除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。
  3. 为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
  4. 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

2. 创建线程池

  • 通常,线程池都是通过 线程池工厂 创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法Executors:线程池创建工厂类
  • public static ExecutorService newCachedThreadPool()
    • 创建一个可根据需要创建新线程 的线程池,但是在以前构造的线程可用时将重用它们 (可变)
    • 对于执行很多短期异步(短但是频繁) 任务的程序而言,这些线程池通常可提高程序性能
    • 如果现有线程没有可用的,则创建一个新线程并添加到池中
    • 终止并从缓存中移除那些已有 60 秒钟未被使用的线程(折中)
  • public static ExecutorService newFixedThreadPool(int nThreads)
    • 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
    • 如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待
  • public static ExecutorService newSingleThreadExecutor()
    • 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程
    • 可保证顺序地执行各个任务
  • 以上方法返回了一个ExecutorService,该对象表示一个线程池,它可以执行 Runable对象代表的线程。

3. 提交任务

  • Runnable 接口
  • Callable 接口类似于 Runnable,用来指定线程的任务。其中的 call() 方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
  • ExecutorService:线程池类
  • <T> Future<T> **submit**(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
  • Future接口:用来记录线程任务执行完毕后产生的结果。
    • get()获取 Future对象中封装的数据结果
  • void shutdown():启动一次顺序关闭,执行以前提交的任务,但 不接受新任务
  • shutdownNow()方法: 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

4. 使用线程池中线程对象的步骤:

  • 创建线程池对象
  • 创建 Runnable 接口/Callable接口 子类对象
  • 提交 Runnable 接口/Callable接口 子类对象
  • 关闭线程池
public class ThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建一个newCachedThreadPool
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //操作线程池
        //向线程池提交任务
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello, thread pool");
            }
        });

        //Callable接口的使用
        Future<Integer> future = executorService.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                TimeUnit.SECONDS.sleep(3);
                int sum = 0;
                for (int i = 0; i < 10; i++) {
                    sum += i;
                }
                return sum;
            }
        });
        System.out.println(future);
        System.out.println(future.get());
    }
}

四、定时器 Timer

1. 概述

  • 调度定时任务,帮助我们在稍后的时刻执行定时任务。一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务 执行一次,或者 定期重复执行

2. TimerTask 定时任务

  • 定时任务:TimerTask对象表示定时任务
  • Timer调度定时任务,定时任务的内容由TimerTask的run方法 运行决定
  • 在 timer 中所有的定时任务都是运行在同一个线程中
  1. cancel 取消定时任务
  • 如果利用TimerTask的cancel要取消定时任务,在定时任务已经开始运行时, 调用cancel方法,是没有效果的
    利用Timer的cancel方法取消定时任务,其实是终止Timer本身
public class TimerDemo {
    public static void main(String[] args) {
        //定义一个定时器
        // 在timer中所有的定时任务都是运行在同一个线程中
        Timer timer = new Timer(); //默认不是守护线程

        //在定时器上调度,定时任务
        timer.schedule(new MyTimerTask(), 3000);
        timer.schedule(new MyTimerTask(), 1000);

        //调度器重复执行定时任务
        timer.schedule(new MyTimerTask(timer), 0, 2000);
    }
}

class MyTimerTask extends TimerTask {
    Timer timer;
    public MyTimerTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        //在输出之前,取消定时任务
        //cancel();
        //利用Timer的cancel方法取消定时任务,其实timer的cance方法,是终止Timer本身
        timer.cancel();
        System.out.println("hello timer");
    }
}

五、多线程和异常

  • 在 Thread 类中,不可以抛出编译型异常,但是可以抛出运行时异常
  • 当 运行时异常抛出线程(溢出线程),能不能捕获?

java语言,并不能直接用try-catch代码块捕获,溢出线程的异常,这是java语言实现上的一个遗憾,但是,并不是说,溢出线程的异常开发者就没办法捕获了。—> Thread.UncaughtExceptionHandler

  • static setDefaultUncaughtExceptionHandler,处理所有线程中未捕获的异常(溢出线程的异常)
  • setUncaughtExceptionHandler,处理某一个线程中的未捕获的异常(溢出线程的异常)
public class supplement01 {
    public static void main(String[] args) {
        //我们可以通过设置UncaughtExceptionHandler对象,让该对象捕获溢出线程的异常
        // callback 回调
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                // 这个方法,会在日志模块 -》 将这些异常信息,通过流保存到文件中
                //处理溢出线程的异常
                System.out.println("DefaultUncaughtExceptionHandler");
            }
        });

        //在主线程中捕获异常
        CatchThreadException1 catchThreadException = new CatchThreadException1();
        catchThreadException.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("Thread t" + e.getMessage());
            }
        });

        try{
            //试图在主线程捕获子线程抛出的异常
            catchThreadException.start();
        }catch (Exception e) {
            System.out.println("我捕获到了异常");
        }
    }
}

class CatchThreadException extends Thread {
    @Override
    public void run() {
        //将异常抛出run方法之外,就等价于将该抛出了线程
       throw new RuntimeException("测试抛出线程的异常");
    }
}

class CatchThreadException1 extends Thread {
    @Override
    public void run() {
        //将异常抛出run方法之外,就等价于将该抛出了线程
        throw new RuntimeException("测试抛出线程的异常");
    }
}
Java进阶学习之路 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。提示:以下是本篇文章正文内容,下面案例可供参考java语言是SUN(Stanford University Network,斯坦福大学网络公司 ) 1995年推出的一门高级编程语言,是一种面向Internet的编程语言。Java一开始富有吸引力是因为Java程序可以在Web浏览器中运行。这些Java程序被称为Java小程序(applet)。 阅读详情

相关推荐

Java多线程编程:从基础到进阶的实战指南

多线程编程是指在一个程序中运行多个线程,以实现并行处理的编程技术。在Java中,每个线程都是一个独立的执行路径,彼此共享同一个进程的内存空间。这种并行处理能够提高程序的响应速度和处理能力,但也带来了线程同步、数据竞争等复杂问题。

Luffy的博客 1260

2021金九银十Java大厂面试题来袭

零基础如何学习Java? 首先,你要明白一点,Java入门不难! 无论你是从事哪个行业,兴趣一定是最好的老师,也是你学习的动力。 学习方式1:自学 自学模式其实我个人不建议绝大部分的人选择,因为自学是需要比较强的自制力以及约束力,例如今天计划自学三小时,但因为一个电影新出,就决定先看完电影再说,一个游戏邀约,就决定先打一把游戏再说,一个“今天已经很累了”,就放弃了这一天的学习计划,而很不凑巧的是,现在大部分年轻人都是如此。所以自学的人往往花费很多时间成本,最后达到的效果或许也不是很满意。 但是如果你一定要选

m0_56830725的博客 253

26、JAVA进阶——多线程

一、认识进程和线程 | 1. 进程 | 2. 线程 | 3. 多线程的优势 | 二、编写线程类 | 三、线程状态和线程调度 | 1.线程的状态 | 2.线程调度

不迁怒,不贰过。小知识,大智慧。 1233

金九银十面试季面试必问JVM性能优化?快来查漏补缺了(含答案)

欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。 微信公众号:慕容千语的架构笔记。欢迎关注一起进步。 JVM面试题集锦文末有提供答案 Java 类加载过程? 描述一下 JVM 加载 Class 文件的原理机制 Java 内存分配。 GC 是什么? 为什么要有 GC? 简述 Java...

玩赚AI大模型的博客 198

ANSYS Sherlock新手避坑:从ODB++文件导入到属性匹配的完整流程(附官方教程路径)

本文详细解析了ANSYS Sherlock从ODB++文件导入到属性匹配的完整流程,帮助新手避开常见陷阱。内容包括环境准备、官方教程使用技巧、ODB++导入关键检查点、属性验证策略及常见报错解决方案,特别强调了路径设置和材料匹配的重要性,提升仿真效率。

weixin_42519781的博客 456

Pikachu靶场环境搭建(Windows环境下安装详细教程)

Pikachu靶场是一个漏洞测试练习平台。其中包含了常见的web安全漏洞,如果你是一个渗透测试学习者没有靶场的练手,那么pikachu是一个很好的测试平台。

weixin_42857656的博客 1829

2020年史诗级‘面试宝典’横空而出,金九银十就靠“它”涨薪了

或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信你一定不会“败”的很惨,因为有了它,足以应对目前市面上绝大部分的 Java 面试了,因为这篇文章不论是从深度还是广度上来讲,都已经囊括了非常多的知识点了。 凡事预则立,不预则废。能读到这里的人,我相信都是这个世界上的“有心人”,还是那句老话:上天不负有心人!我相信你的每一步努力,都会收获意想不到的回报。 包含的模块 本文分为十九个模块,分别是:Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spr.

m0_50180963的博客 1597

ESP32学习,驱动LED点阵屏,SD卡播放GIF动图

搬运就可以实现效果,如果有不足,请批评指正。下步该学一下如何文章上视频了,老是截图不方便展示效果。

u011625956的博客 1868

Java-进阶:多线程2

目录 一、Lock 接口 二、线程间的通信 三、线程池 四、定时器 Timer 五、多线程和异常 一、Lock 接口 1. 线程锁 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁 //锁的使用 Lock l = new ReentrantLock(); //创建一个锁对象 l.lock(); try { // access the resource protected by this lock } fi

m0_57081562的博客 82

处理SAP资产折旧AFAB 过账报错:“科目 8019010100 要求一个成本会计分配”

本文介绍如何处理资产折旧报错 ““科目XXXXXXX 要求一个成本会计分配””

IT小职员 2777

K8S搭建(centos)完整版

每个人都有惰性,但不断学习是好好生活的根本,共勉!

感谢阅读 4150

Java-进阶:多线程1(2)

1. 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 等阻塞方法处于阻塞状态,它还将收到一个 InterruptedException。虽然实现线程有两种方式,其实从客观来讲,线程本身只代表独立的执行路径, 执行的具体内容其实是Task本身,和执行路径的实现本身没有联系;

2401_84103216的博客 920

【ARM CoreLink 系列 4.2 -- NI-700 Function units 详细介绍】

CoreLink NI-700的ASNI(AXI系统网络接口)完成者单元负责接收并处理来自AXI请求者设备的请求。这些单元将事务打包成根据NI-700通用传输(GT)协议的Flits,并将GT响应Flits解包成AXI响应。AXI与GT协议之间的请求、数据和响应事务的转换:确保AXI事务可以被NI-700网络正确处理,并将GT协议的响应正确转换回AXI响应。入站事务的突发拆分。

CodingCos的博客 1324

Java-进阶:多线程2,linux软件架构图

三、线程池 四、定时器 Timer 五、多线程和异常 一、Lock 接口 1. 线程锁 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁 //锁的使用 Lock l = new ReentrantLock(); //创建一个锁对象 l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); } Loc

m0_64384202的博客 164

倒立摆的PLC控制(模型分析+ SCL源代码)

首先简单介绍下倒立摆模型,下面这幅图是MATLAB网站上的倒立摆模型,下面我们利用牛顿第二运动定律建立摆杆的运动方程。

RXXW_Dor的博客 3336

SAP Joule 经过了超过 2.5 亿行 ABAP 代码的训练之后......

披露的统计数据,Joule 提供的这种全新系统交互方式,可将事务性任务执行速度提高 90%,访问 SAP Help Portal 内容的速度提高 95%。在 ABAP 应用开发领域,SAP 官网数据显示,Joule 经过超过 2.5 亿行 ABAP 代码的训练,有信心成为这一领域行业领先的代码助手。刚刚过去的 SAP 蓝宝石大会上,SAP 提到,从2023年第四季度以来,SAP 已经交付了 50 项基于人工智能的创新。让其生成 SAP 应用能够识别的结构化应用数据,业务用户全程无需离开 SAP 应用程序。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 803

java进阶:15.2 多线程 - synchronized、线程安全类

如果一个类的对象在多线程程序中没有导致竞争状态,则称这样的类为线程安全的( thread-safe) 。 1. 线程同步

桃子味的梨 346

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Java进阶(二)多线程2:生命周期、线程安全、同步

3.线程的生命周期 4.线程的同步 5.线程的通信 6.JDK5新增线程创建方式

qq_33491651的博客 229

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

javaSE初阶————多线程进阶(2)

另外,记录的元素个数size怎么办呢,两个线程同时增加数据,size也会有线程安全问题,还有加锁吗,忘了我们的AtomicIngter了吗,这个原子类也是很好用的呀,大家不要忘了;我们之前学过Runnable接口,它是一个任务,我们可以在创建线程的时候把任务丢给线程使用匿名内部类等方法来完成创建对象,现在我们有了一个新的方法来创建任务,并且执行这个任务,就是我们的Callable接口,Runnable的run方法是没有返回值的,但是Callable提供了返回值,支持泛型,我们就能获取到我们想要的参数,

2301_79083481的博客 2680

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Java进阶12 多线程

线程、进程、开启线程的三种方式、线程安全和同步、线程通信、生产者消费者模式

summer_9823的博客 1237

Java多线程进阶详解

使用CAS编写代码,比较然后交换。大部分没问题,但在极端情况下有问题。比如银行取钱一个人账户是1000,取款500,假设是按照CAS的方式来执行的。按取款卡了一下,又按了一下。就有两个线程来进行操作,如果恰好有人转账500,则会出现bug。如何处理ABA问题呢?使用账户余额判定,本身就不科学。属于能加也能减。引入一个只能加的版本号,每次取钱或者存钱,版本号就+1,就可以进一步解决ABA问题。CAS的核心就在于说,通过数值没变=》中间没有别的线程覆盖。

2302_80113478的博客 1149

Java多线程之进阶篇

ThreadPoolExecutor是线程池类。对于线程池,可以通俗的将它理解为“存放一定数量的一个线程集合。线程池允许若个线程同时运行,运行同时运行的线程数量就是线程池的容量。当添加到线程池中的线程超过它的容量时,会有一部分线程阻塞等待,线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理。(2)

weixin_48374562的博客 969

Java进阶之多线程任务Callable:用法实例(一百零八)

本篇目的:Java进阶之多线程任务Callable:用法实例基本概念Callable是一个函数式接口,表示一个可以返回结果的任务。Callable接口允许实现类定义一个带返回值的方法call,用于表示任务的执行逻辑。功能定义任务的执行逻辑,并返回结果。允许任务被线程执行,并获取任务的返回值。使用限制Callable接口的call方法可以抛出受检查的异常。Callable任务需要被线程执行,通常通过FutureTask或线程池来管理。性能特性Callable。

Android系统攻城狮 1658

Java进阶篇之多线程

多线程是提升应用性能、提高并发能力的强大工具,但同时它也带来了复杂的同步与调度问题。理解和掌握线程的生命周期、同步机制、线程池等概念,将有助于你写出高效、稳定的并发程序。👨‍💻👩‍💻今天我们通过示例代码和对比分析,深入了解了 Java 多线程的基本概念和常见技巧,掌握了如何通过线程池、高效的同步机制以及合理的设计来提升程序性能。🚀在接下来的文章中,我们将继续探讨Java中线程的创建和运行以及其他重要特性,敬请期待!👉😊。

code2cat的博客 917

JAVA多线程进阶

下面是实现原子类的伪代码,此处的 oldValue 在实际实现中是直接用寄存器存储的内容 ,value 是内存中的原始数据,CAS(value,oldValue,oldValue+1) 操作理解为,判定内存中的值和寄存器中的值是否一致,如果一致,就把内存中的value值设置为 oldValue+1,返回 true,循环结束;CAS的关键是先比较,再交换,在比较的过程中,当前值和旧值是相同时,就认为中间没有发生过改变,但是可能存在中间发生了改变,但多次改变后值不变的情况。读锁和读锁之间不存在互斥关系;

2301_81831660的博客 1071
上一篇: Java 进阶:多线程1
下一篇: Java 数据结构:什么是树?怎么使用?
JavaCaiy
博客等级 码龄6年 1万+粉丝 95原创
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值