线程池 单个请求处理的时间很短,海量请求的情况下,频繁的创建线程,销毁线程所带来的系统开销是巨大的。 降低频繁创建、销毁线程的开销、线程的创建和销毁需要 JVM 进行大量的辅助操作。(内存的分配与回收、还会给垃圾回收器带来压力) “池”的概念可以很好的防止资源不足。过多线程会占用大量内存,导致OOM。 加快响应速度(复用池中的线程) 合理利用CPU和内存。 统一管理资源。 线程池适用的场景 服务器接受到大量请求时,使用线程池技术是非常适合的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。 5个以上的线程就可以使用线程池来管理。 不使用线程池(海量任务) 线程的创建和销毁需要 JVM 进行大量的辅助操作。(内存的分配与回收、还会给垃圾回收器带来压力) 在Java语言中每创建一个线程直接对应操作系统中的一个线程。在操作系统中频繁创建、销毁大量线程会造成很大的系统开销。操作系统支持创建的线程数是有上限的。(线程数量无法与未知的任务数量一一对应) 无法作用于C10K场景,会引发OOM异常。 public class EveryTaskOneThread { publi....
CountDownLatch 创建 CountDownLatch 实例的时候需要传入线程数,await()操作进入等待状态,每个线程执行完毕调用 countDown(),计数器减一,当计数器为 0 的时候处于 WAITING 状态的线程会被唤醒。 应用场景:启动三个线程计算,需要每个线程的计算结果进行累加。 CountDownLatch import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(8); for (int i = 0; i < 8; i++) { int finalI = i; new Thread(()->{ try { Thread.sleep(finalI * 1000L); System.out.println(Thread.currentThread().getName....
原子类 原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割。 一个操作是不可中断的,即便是在多线程情况下也可以保证。 java.uti.concurrent.aomic包下有很多具有原子特性的类。 原子类 VS 锁 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况,锁的粒度通常大于原子类。 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况下。 6类原子类纵览 基本类型原子类 以 AtomicInteger 为例,本质是对 Integer 的封装,提供原子的访问和更新操作,其本质是基于 CAS 技术。 AtomicInteger 常用方法 * get() 获取当前的值 * getAndSet() 获取当前的值,并设置新的值 * getAndIncrement() 获取当前的值,并自增 * getAndDecrement() 获取当前的值,并自减 * getAndAdd() 获取当前的值,并加上预期的值 * compareAndSet() 如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update....
线程安全 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象时线程安全的。----《并发编程实战》 主要是两个问题 数据争用:多个线程同时修改共享数据,会造成错误数据。(原子性) 竞争条件:操作顺序造成的问题,例如:读取发生在写入之前。(可见性、重排序) 线程安全带来的性能开销 运行速度、设计成本(增加编码的复杂度)、trade off 线程安全问题分类 运行结果错误:a++ 多线程下出现消失的请求现象 活跃性问题:死锁、活锁、饥饿 对象发布和初始化的时候的安全问题:由于顺序源于依然会造成错误,比如在写入之前就读取了。 * 发布:一个对象被声明为public,它就是被发布出去了,或者return 对象,或者方法传参 * 初始化 * 逸出: 1、方法返回了一个 private 对象(private 的本意是不让外部访问,这样就没人可以访问此对象了)。 2、还未完成初始化(构造函数完全执行完毕)就把对象提供给外界,比如: 在构造函数中为初....
线程的创建方式:继承 Thread run()整个都被重写 任务与线程类高度耦合。 每次新建任务都需要创建独立的线程,如果使用 Runnable 则可以利用线程池,大大减少创建线程和销毁线程的开销。 Java 只允许单继承,影响扩展性。 public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.setName("线程demo"); myThread.start(); } } 线程的创建方式:实现 Runnable 最终调用 target.run() 任务与线程类解耦 可扩展 解决资源开销(利用线程池) public class MyRunable implements Runnable,Serializable {....
并发简史 早期计算机(不包含操作系统),只能从头到尾执行一个程序,并且执行的程序能访问计算机中的所有资源。 操作系统的出现使得计算机每次能运行多个程序,并且不同程序都在单独的进程中运行:操作系统为各个独立执行的进程分配各种资源,包括内存、文件句柄以及安全证书等。计算机中加入操作系统来实现多个程序的同时执行,主要是基于以下原因: 资源利用率(同步等待造成的资源浪费) 公平性(通过时间分片实现用户和程序共享计算机资源) 便利性(多程序开发可以降低单个程序的开发的复杂度) 串行与并行的区别 好处:可以缩短整个流程的时间 串行(5):洗茶具 => 打水 => 烧水(同步等待) => 等水开 => 冲茶 并行(4):打水 => 烧水同时洗茶具 => 水开 => 冲茶 可怕的现实:摩尔定律的失效 摩尔定律:当价格不变时,集成电路上可容纳的元器件的数目,约每隔 18-24 个月便会增加一倍,性能也将提升一倍。这一定律揭示了信息技术进步的速度。 然而,在 2004 年,Intel 的 4GHz 芯片宣布推迟到 2005 年,然后再 200....
业务需求 排行榜功能是一个很普遍的需求。使用 Redis 中有序集合的特性来实现排行榜是又好又快的选择。 一般排行榜都是有实效性的,比如“用户积分榜”,游戏中活跃度排行榜,游戏装备排行榜等。 需求面临的问题 数据库设计复杂。 并发数较高。 数据要求实时性高。 Redis 相关 API 概述 封装 Redis 工具类 RedisService Service 层(RangingService 使用 Redis 工具类) import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; import java.util.LinkedHashMap; import java.util.List; im....