博客
关于我
JUC高级多线程_01:基础知识回顾
阅读量:701 次
发布时间:2019-03-21

本文共 2974 字,大约阅读时间需要 9 分钟。

JUC揭秘:从康格机制到现代进程管理

Java concurrency 为开发者提供了强大的工具包,#JUC 是java.util.concurrent包下的一大核心模块,涵盖了并发、锁、原子包等多个重要组成部分。作为开发者,你可能会不时地在项目中运用JUC中的组件,比如ReentrantLock、CountDownLatch等。这些工具类不仅简化了并发编程的实现,也为多线程开发提供了更高效的资源管理方式。

进程与线程的基础知识

在操作系统中,进程和线程是资源管理的两个不同的层级。简单来说,进程是资源分配和执行的基本单位,而线程则是更细粒度的执行单元。每个进程都至少包含一个线程,而线程则在进程中独立执行任务,占用更少的系统资源。

进程管理的例子:比如电脑运行多个程序(如浏览器、文档编辑器),每个程序都代表一个独立的进程。线程管理的例子:使用浏览器时,后台可能有多个线程分别负责渲染、JavaScript引擎等不同的功能。

并发与并行的辨别

现今的应用程序越来越多地面临资源竞争问题。并发就意味着在同一时间内有多个线程同时试图访问同一资源,这往往会导致资源争夺的风险。比较典型的案例是电商平台上的秒杀活动,数十万甚至数百万的用户同时进入购物,导致系统资源瞬间被压榨,甚至引发服务崩溃。

而并行则是一种更松散的概念,强调多项工作可以同时进行,最终再将结果汇总到一起。比如泡方便面时,你同时烧水、撕开方便面包装、倒调料,这些动作可以并行进行,最终形成一个完整的早餐。

wait与sleep的区别

在并发编程中,线程的状态分为多种状态,像NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED等。其中,wait 和 sleep 是最常用的两种状态。

的区别主要在于是否占用锁。

  • wait:释放锁,线程进入等待状态,不再占用任何资源,直到被通知或超时。
  • sleep:不释放锁,线程仍然占用资源,只是进入睡眠状态,等待CPU重新唤醒。

理解这点至关重要,因为这直接影响到线程之间的资源共享和锁的管理,是在编写高并发程序时不可忽视的知识点。

线程的六大状态

线程的状态机制非常重要,能够帮助我们更好地理解线程行为。具体来看,线程可能经历以下六种状态:

  • NEW:线程一经创建就进入这个状态,准备好接收任务。
  • RUNNABLE:线程处于可以立即执行任务的状态,常见于调度器或线程池的等待状态。
  • BLOCKED:线程因为没有获取到所需的资源而进入阻塞状态,需要等待其他线程完成当前的操作。
  • WAITING:线程在等待某个同步操作(如信号或事件)完成,不会占用资源。
  • TIMED_WAITING:线程等待一段时间后才自动唤醒,如果在这段时间内没有任务通知,则线程会被销毁。
  • TERMINATED:线程的执行完毕,可能是正常退出也可能是被中断。
  • 售票系统优化方案

    在售票系统优化方面,使用JUC组件可以显著提升系统性能。以下是一个经典的售票系统优化案例:

    挑战描述:三个售票员需要同时卖出30张票,保证每个用户的购票顺序唯一且高效。

    优化方案

  • 资源类设计:将售票数和卖票逻辑封装在资源类中,确保高内聚。
  • 线程与锁结合:创建三个线程,分别负责不同的售票员。使用ReentrantLock作为锁,保证每次只允许一个线程访问资源。
  • 系统级优化:使用线程池来管理线程,避免线程资源不够用的问题。
  • 代码示例:

    public class JUC01_saleTicket01 {    public static void main(String[] args) {        Ticket01 ticket = new Ticket01();                new Thread(() -> {            for (int i = 1; i <= 30; i++) {                ticket.sale();            }        }, "Thread_A").start();        new Thread(() -> {            for (int i = 1; i <= 30; i++) {                ticket.sale();            }        }, "Thread_B").start();        new Thread(() -> {            for (int i = 1; i <= 30; i++) {                ticket.sale();            }        }, "Thread_C").start();    }}class Ticket01 {    private int number = 30;    private Lock lock = new ReentrantLock();        public void sale() {        lock.lock();        try {            if (number > 0) {                System.out.println(Thread.currentThread().getName() + " 卖出第:" + (number--) + "票,还剩下:" + number + "张");            }        } catch (Exception e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }}

    Lambda表达式解析

    在Java 8时代,Lambda表达式为我们提供了更加简洁、灵活的手段来定义线程任务。在上面的代码中,我们使用Lambda表达式简化了线程创建逻辑:

    new Thread(() -> {    for (int i = 1; i <= 30; i++) {        ticket.sale();    }}, "Thread_A").start();

    Lambda表达式的三个关键步骤

  • 拷贝小括号:照抄方法的参数列表,省略数据类型。
  • 写死右箭头:将Lambda体括起来。
  • 落地大括号:写入Lambda的执行逻辑。
  • 此外,函数式接口(@FunctionalInterface)为Lambda表达式的应用提供了更广阔的应用场景。

    补充:线程池与JUC

    在实际开发中,线程池是一个更高级的资源管理方式。通过线程池,我们可以轻松地进行并发编程,而不必手动管理每一个线程。JUC提供的线程池接口包括:

    • ExecutorService:基础线程池接口,负责任务的提交和执行。
    • ThreadPoolExecutor:带有积极策略的线程池,能够动态调整线程数量。
    • ScheduledThreadPoolExecutor:支持定时任务的线程池。

    这些组件结合起来,能够帮助开发者高效地管理系统中的并发任务。

    转载地址:http://uklez.baihongyu.com/

    你可能感兴趣的文章
    Android简单MVP解析接口列表,搜索框,点击切换
    查看>>
    ADB
    查看>>
    Ubuntu配置Git,ssh密钥
    查看>>
    响应的HTTP协议格式+常见的响应码
    查看>>
    javaweb中servlet请求的转发
    查看>>
    Java数组
    查看>>
    创建线程方式
    查看>>
    线程池
    查看>>
    Netty读写方法
    查看>>
    LRUCache
    查看>>
    Mac上如何强制关闭应用
    查看>>
    SpringBoot @Value与@ConfigurationProperties比较
    查看>>
    关于Linux系统中touch命令的说明
    查看>>
    剑指Offer03-数组中重复的数字
    查看>>
    将windows里的内容直接复制粘贴到ubuntu,提高效率
    查看>>
    将tomcat设置成window自启动服务
    查看>>
    GC overhead limit exceeded
    查看>>
    mysql高可用
    查看>>
    17蓝桥试题之承压计算
    查看>>
    webservice 远程服务器返回错误:(400)错误的请求
    查看>>