带你快速搞定java多线程(3)

目录
  • 一、锁的概念
  • 二、synchronized 的使用方式
  • 三、synchronized 的实现原理列
    • 小结
  • 四、线程池是什么
  • 五、为什么要用线程池?
  • 六、看下类图,从整体上理解下
  • 七、线程池的创建
  • 八、线程池核心参数说明
  • 九、几个疑问点
    • 9.1、是怎么保证线程不销毁的?
    • 9.2 提交任务有哪几种方式?
    • 9.3 拒绝策略都有哪些?
    • 9.4 线程池的关闭
    • 9.5 初始化线程池时线程数的选择
  • 十、总结

一、锁的概念

先来聊聊这几个概念,总不能聊起来的时候啥也不知道,只知道干活也没有用。

公平锁:当线程A获取访问该对象,获取到锁后,此时内部存在一个计数器num+1,其他线程想访问该对象,就会进行排队等待(等待队列最前一个线程处于待唤醒状态),直到线程A释放锁(num = 0),此时会唤醒处于待唤醒状态的线程进行获取锁的操作,一直循环。如果线程A再次尝试获取该对象锁时,会检查该对象锁释放已经被占用,如果还是当前线程占用锁,则直接获得锁,不用进入排队。

非公平锁:当线程A在释放锁后,等待对象的线程会进行资源竞争,竞争成功的线程将获取该锁,其他线程继续睡眠。

公平锁是严格的以FIFO的方式进行锁的竞争,但是非公平锁是无序的锁竞争,刚释放锁的线程很大程度上能比较快的获取到锁,队列中的线程只能等待,所以非公平锁可能会有“饥饿”的问题。但是重复的锁获取能减小线程之间的切换,而公平锁则是严格的线程切换,这样对操作系统的影响是比较大的,所以非公平锁的吞吐量是大于公平锁的,这也是为什么JDK将非公平锁作为默认的实现。

悲观锁:总是假设最坏的情况,每次想要使用数据的时候就恰好别人也要修改数据,一切是以安全第一,所以在每次操作资源的时候都会先加锁,不管有没有人抢,然后独占资源。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现

乐观锁:乐观锁和悲观锁刚好相反,假定自己使用资源的时候没有人抢,所以不需要上锁。乐观锁的实现方案一般来说有两种:版本号机制 和 CAS实现 。下期可能会讲。

在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

二、synchronized 的使用方式

场景 具体分类 锁对象 代码示例
修饰方法 实例方法 当前实例对象 public synchronized void method () { ... }
... 静态方法 当前类的Class对象 public static synchronized void method () { ... }
修饰代码块 代码块 ( )中配置的对象 synchronized(object) { ... }

三、synchronized 的实现原理列

想知道原来先去底层看下,看看字节码是什么样子的,let's go!

  private static Object lock = new Object();
  public static synchronized void testSyn() {
      System.out.println("香菜");
  }
  public synchronized void testSyn2() {
      System.out.println("香菜");
  }
  public static void testObj() {
      synchronized (lock) {
          System.out.println("香菜");
      }
  }

看下字节码:

可以看到synchronized 的地方使用的是monitorenter指令,每个对象都和一个monitor对象关联,主要用来控制互斥资源的访问,如果你想要加锁必须先获得monitor的批准,如果现在正有线程访问,会把申请的线程加入到等待队列。

小结

1、 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对class对象的锁,该类所有的对象同一把锁。2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制,避免做嵌套synchronized 的使用。

4、synchronized 要尽量控制范围,不能范围太大,否则会损失系统性能。

四、线程池是什么

线程池就是一个对象持有一堆线程,举个例子就是饿了么养的骑手团队。线程池就是这个团队,每个骑手都是一个线程。

五、为什么要用线程池?

假如现在商家有外卖单子,需要骑手去送单,这个时候的外卖任务就会派单给骑手,为什么要用线程池呐?

有几个好处,第一就是骑手的招聘是有成本的,等你有了外卖订单再去招聘,来不及了,不如平常养一些骑手,线程的创建和销毁的开销是巨大的。

第二就是不能一个单子来了就来一个骑手,这样的话骑手的数量很难控制,对于派单来说也存在很大的压力,会造成整个骑手团队的崩溃,对应的就是可以通过线程池控制系统内的线程数量,有效的避免大量的线程池争夺CPU资源而造成堵塞。

第三如果养了一个骑手团队,这样在骑手的管理上可以规范,以便提供更好的外卖服务,比如这种外卖超时,骑手打星等。对比线程池就是线程池可以提供定时、定期、单线程、并发数控制等功能。

六、看下类图,从整体上理解下

七、线程池的创建

线程池主要使用的四种

固定数量的线程池(FixedThreadPool

定时线程池(ScheduledThreadPool

可缓存线程池(CachedThreadPool

单线程化线程池(SingleThreadExecutor

八、线程池核心参数说明

首先看下如何构造一个线程池

  public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
      return new ThreadPoolExecutor(nThreads, nThreads,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory);
      public ThreadPoolExecutor(int corePoolSize,
                            int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory,
                            RejectedExecutionHandler handler)

核心参数说明:

九、几个疑问点

9.1、是怎么保证线程不销毁的?

核心线程会阻塞等待workQueue

9.2 提交任务有哪几种方式?

9.3 拒绝策略都有哪些?

拒绝策略(handler)当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现RejectedExecutionHandler接口,并实现rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不过Executors框架已经为我们实现了4种拒绝策略:

AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。

CallerRunsPolicy:由调用线程处理该任务。

DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。

DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

9.4 线程池的关闭

关闭线程池可以调用shutdownNow和shutdown两个方法来实现

shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表。

shutdown:当我们调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务。

9.5 初始化线程池时线程数的选择

如果任务是IO密集型,一般线程数需要设置2倍CPU数以上,以此来尽量利用CPU资源。

如果任务是CPU密集型,一般线程数量只需要设置CPU数加1即可,更多的线程数也只能增加上下文切换,不能增加CPU利用率。

具体问题具体分析。

十、总结

线程池是项目中常用的,需要理解线程池的应用场景和构造函数,正确的使用线程池。

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 彻底搞懂Java多线程(三)

    目录 Java线程池 线程池的优点 线程池的6种创建方式 创建单个线程池的作用是什么? 线程池的第七种创建方式 ThreadPoolExecutor的执行方式 ThreadPoolExecutor的执行流程 线程池的终止 线程池的状态 异步.同步 1.Java 线程 同步与异步 线程工厂 总结 Java线程池 线程的缺点: 1.线程的创建它会开辟本地方法栈.JVM栈.程序计数器私有的内存,同时消耗的时候需要销毁以上三个区域,因此频繁的创建和销毁线程比较消耗系统的资源. 2.在任务量远远大于线程可

  • 彻底搞懂Java多线程(二)

    目录 Java中的锁 1.synchronized锁(jvm层的解决方案,也叫监视器锁) 2.手动锁Lock synchronized锁 synchronized使用场景 1.使用synchronized来修饰代码块(可以给任意的对象进行加锁操作) 2.使用synchronized来修饰静态方法(对当前的类进行加锁的操作) 3.使用synchronized来修饰普通的方法(对当前类的实例来进行加锁) synchronized注意事项 1.加锁的时候一定要使用同一把锁对象 Lock锁使用的注意事项

  • 彻底搞懂Java多线程(五)

    目录 单例模式与多线程 立即加载/饿汉模式 延时加载/懒汉模式 饿汉/懒汉对比 阻塞队列的实现 常见的锁策略 乐观锁 CAS CAS在java中的应用 CAS 的ABA问题 ABA 问题的解决 悲观锁 独占锁.共享锁.自旋锁.可重入锁 详解synchronized锁的优化问题 Semaphore Semaphore的作用: Semaphore实现原理: Semaphore的使用: CountDownLatch\CyclicBarrier CountDownLatch CountDownLatch

  • 彻底搞懂Java多线程(一)

    目录 Java多线程 线程的创建 线程常用方法 线程的终止 1.自定义实现线程的终止 2.使用Thread的interrupted来中断 3.Thraed.interrupted()方法和Threaed.currentThread().interrupt()的区别 线程的状态 线程的优先级 守护线程 线程组 线程安全问题 volatile关键字 总结 Java多线程 线程的创建 1.继承Thread 2.实现Runnable 3.实现Callable 使用继承Thread类来开发多线程的应用程序

  • 彻底搞懂Java多线程(四)

    目录 SimpleDateFormat非线程安全问题 ThreadLocal ThreadLocal的原理 ThreadLocal常用方法 ThreadLocal的初始化 InheritableThreadLocal的使用 总结 SimpleDateFormat非线程安全问题 实现1000个线程的时间格式化 package SimpleDateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java

  • 带你快速搞定java多线程

    目录 1.什么是线程 2.线程的状态 3.怎么通俗理解进程,线程? 4.线程和进程的区别 5.什么是线程安全 6.如何创建线程 总结: 1.什么是线程 线程是操作系统调度的最小单元,也叫轻量级进程.它被包含在进程之中,是进程中的实际运作单位.同一进程可以创建多个线程,每个进程都有自己独立的一块内存空间.并且能够访问共享的内存变量. 2.线程的状态 线程的状态一般看到的也就是Runable 和blocked ,最多的还是blocked,因为cpu的时间片很短,切换的很快等待IO,等待临界资源.大概

  • 带你快速搞定java多线程(4)

    目录 1.AQS 是什么? 2.AQS 模型 3.AQS state 4.AQS 两种资源共享方式: 5.模板方式实现自定义 6.锁的分类:公平锁和非公平锁,乐观锁和悲观锁 7.CAS 8.总结 1.AQS 是什么? AQS 是类 AbstractQueuedSynchronizer的简称,也是常用锁的基类,比如常见的ReentrantLock,Semaphore,CountDownLatch 等等. AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架.是Java提供的一种模板

  • 带你快速搞定java多线程(5)

    目录 1.介绍 2.countdownlantch的用法. 3.如何利用AQS 实现 CountDownLatch 4.总结 1.介绍 CountDownLantch 倒数计时器,一个同步辅助类,一个线程(或者多个),等待另外N个线程完成某个事情后才能执行.用给定的计数初始化CountDownLatch,其含义是要被等待执行完的线程个数. 每次调用CountDown(),计数减1,执行到await()函数会阻塞等待线程的执行,直到计数为0. CountDownLantch 无法重置 2.coun

  • 带你快速搞定java多线程(3)

    目录 一.锁的概念 二.synchronized 的使用方式 三.synchronized 的实现原理列 小结 四.线程池是什么 五.为什么要用线程池? 六.看下类图,从整体上理解下 七.线程池的创建 八.线程池核心参数说明 九.几个疑问点 9.1.是怎么保证线程不销毁的? 9.2 提交任务有哪几种方式? 9.3 拒绝策略都有哪些? 9.4 线程池的关闭 9.5 初始化线程池时线程数的选择 十.总结 一.锁的概念 先来聊聊这几个概念,总不能聊起来的时候啥也不知道,只知道干活也没有用. 公平锁:当

  • 带你快速搞定java多线程(2)

    目录 1.Future的类图结构,从整体上看下Future的结构 2.future的使用,说的再多都么什么用,来个例子悄悄怎么用的. 3.通俗理解 4.原理 5.总结 1.Future的类图结构,从整体上看下Future的结构 首先看下future接口的函数,共有5个方法. get() 获取执行的结果,另外一个重载是有时间限制的get ,如果超时会有异常 isDone() 判断future 结果是否处理完成 cancel 取消任务 2.future的使用,说的再多都么什么用,来个例子悄悄怎么用的

  • 带你快速搞定java IO

    目录 一.IO底层是怎么回事? 二.梳理类的结构 三.IO类大点兵 四.来波实例展示 1.访问操作文件(FileInputStream/FileReader ,FileOutputStream/FileWriter) 2.缓存流的使用(BufferedInputStream/BufferedOutputStream,BufferedReader/BufferedWriter) 3.获取键盘输入 总结: 一.IO底层是怎么回事? 操作系统就是管家,电脑的设备就是资源,如果进程先要操作资源,必须要进

  • 带你快速搞定java并发库

    目录 一.总览 二.Executor总览 三.继承结构 四.怎么保证只有一个线程 五.怎么保证时间可以定时执行 六.使用 总结 一.总览 计算机程序 = 数据 + 算法. 并发编程的一切根本原因是为了保证数据的正确性,线程的效率性. Java并发库共分为四个大的部分,如下图 Executor 和 future 是为了保证线程的效率性 Lock 和数据结构 是为了维持数据的一致性. Java并发编程的时候,思考顺序为, 对自己的数据要么加锁.要么使用提供的数据结构,保证数据的安全性 调度线程的时候

  • 带你快速搞定java数组

    目录 1.数组的定义 2.array 遍历 3.List和array 之间的转换 1.数组转list 2.list 转数组 3.Arrays工具类 4.可能遇到的问题 总结 1.数组的定义 先声明后使用 数据类型 [] 数组名称 = new 数据类型[长度];String[] arr3 = new String[5]; 数据类型 数组名称[] = new 数据类型[长度];String arr[] = new String[5]; 直接初始化 String[] arrs = {"1",

  • 带你轻松搞定Java面向对象的编程--数组,集合框架

    目录 一.数组 1.数组的定义 2.数组的声明 3.数组的初始化 二.集合概述 三.Collection接口 1.Collection接口概述 2.集合框架的三个组件 3.Iterator接口 四.List接口 1.ArrayList类 2.LinkedList类 五.Set接口 1.HashSet类 六.Map接口 1.HashMap类 七.泛型 总结 一.数组 1.数组的定义 数组是为了解决同类数据整合摆放而提出的,可以理解为一组具有相同类型的变量的集合,它的每个元素都具有相同的数据类型.

  • 带你快速搞定Mysql优化

    目录 1.查询语句的执行顺序 2.数据类型的选择 3.索引优化 主键索引 多列索引 4.查询性能优化 1.查询的生命周期 2.SELECT语句尽量指明查询字段名称 3.小表驱动大表 总结 1.查询语句的执行顺序 select[distinct]   from   join(如left join)   on   where   group by   having   union   order by   limit 执行顺序: from where 聚 having order limit 1.f

随机推荐