多线程

作者: 汤圆叔 | 来源:发表于2018-07-03 19:25 被阅读4次

多线程的具体应用场景有哪些?实际中需要注意些什么?

  • 后台任务,例如:发短信、发邮件、发MQ消息、记录日志。
  • 并行计算,例如:调度任务处理、处理海量数据(统计用户历史订单消费总额,计算加盟商月度服务费等)。
  • WEB开发,例如:Servlet容器本身就是多线程环境,只是由于大部分情况我们都是基于使用无状态单例Bean处理业务,从而避免了处理线程安全等问题。

多线程可以提高处理能力,增加性能,充分利用服务器资源,但要注意:

  • 线程资源最好通过线程池提供,避免在应用中自行显式创建线程,否则可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题
  • 高并发时,同步调用应该去考量锁的性能损耗:能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
  • 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
  • 并发修改同一记录时,为了避免更新丢失,如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。另外注意:乐观锁的重试次数不得小于3次。
  • 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
  • 子线程抛出异常堆栈,不能在主线程try-catch到,所以子线程执行代码时注意catch异常进行处理。
  • 避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
  • volatile解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。

java concurrent 包你们都用过哪些类?是如何使用的?

常用到的主要有:

  • 线程安全集合类:ConcurrentHashMap、CopyOnWriteArrayList(少写多读场景,配置中心配置存储)、ArrayBlockingQueue(阻塞队列,生产消费模型场景)
  • 原子(CAS无锁化)类:AtomicInteger(多线程环境下获取唯一id)、LongAdder(JDK1.8替代AtomicLong进行计数统计)
  • 线程同步类:CountDownLatch(同步计数屏障)、CyclicBarrier(同步计数屏障可重置)、Semaphore(信号量控制)
  • 锁:ReentrantLock(可重入锁)、ReentrantReadWriteLock(可重入读写锁)
  • 线程池:ThreadPoolExecutor(线程池技术)、Future(线程返回值)

AtomicLong 和 LongAdder 的区别了解吗?

二者都是基于CAS实现的无锁化原子类。二者的区别是LongAdder 相对于AtomicLong 增加了分段CAS机制来降低CAS失败几率,所以在低并发场景下二者性能相当,而高并发场景下 LongAdder 更为高效。其中AtomicLong 更新值时只是简单的通过死循环的进行底层CAS操作,而 LongAdder 是在此基础之上新增了分段CAS累加然后通过对cell数组和base进行求和(调用sum()方法)取得最终结果的机制,极大的降低了各线程CAS时的冲突,与AtomicLong相比,LongAdder更多地用于收集最终统计数据,而不是细粒度的同步控制,无法实时获取原子操作结果。而且,LongAdder只提供了add(long)和decrement()方法,想要使用cas方法还是要选择AtomicLong。

无锁编程(CAS)了解吗?

java.util.concurrent.atomic包下的类通过CAS的机制实现无锁化线程安全。
CAS机制是:我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”,CAS是项乐观锁技术,基于CPU原子指令实现,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

AQS了解吗?原理是什么?

详情参见 Java并发编程--AQS

你们是如何使用线程池的?线程池原理了解吗?

通过线程池处理一些异步操作(发邮件、发短信、发MQ消息)、定时任务处理大批量数据等场景。
ThreadPoolExecutor 线程池在执行excute方法时,主要内容如下:


线程池的处理流程
  • 如果当前运行的线程少于corePoolSize,则创建新线程(Worker)来执行任务(需要获得全局锁)
  • 如果运行的线程等于或多于corePoolSize ,则将任务加入BlockingQueue
  • 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(需要获得全局锁)
  • 如果创建新线程将使当前运行的线程超出maxiumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法根据饱和策略进行处理。

详情参见:Java中的线程池——ThreadPoolExecutor的原理
线程池原理详解

阻塞队列和非阻塞队列了解吗?你们是如何使用的?

  • 阻塞队列:适用于很多生产者-消费者模型场景:记录关键业务日志(通过ArrayBlockingQueue+ThreadPoolExecutor+Mongodb实现,特殊情况数据量过大时降级到本地logback记录再后续处理)、处理超大数据文件(逐行加载(put进阻塞等待)数据到队列中由线程池消费(take出阻塞等待)处理,ArrayBlockingQueue+ThreadPoolExecutor)
    • ArrayBlockingQueue(线程安全的阻塞队列,基于数组实现,读写是同一个锁,吞吐量受限)
    • LinkedBlockingQueue(线程安全的阻塞队列,基于链表实现,读写是两个锁,吞吐量大幅提升,但入队操作始终创建一个Node,高并发时GC可能会影响性能稳定)
  • 非阻塞队列:使用非阻塞队列,虽然能即时返回结果(消费结果),但必须自行编码解决返回为空的情况处理(以及消费重试、高频轮询造成的资源紧张等问题)。实际使用场景较少,Tomcat的NioEndPoint中的每个poller里面维护了一个ConcurrentLinkedQueue<Runnable>用来作为缓冲存放任务。

CountDownLatch、CyclicBarrier 和 Semaphore 的作用分别是什么?有哪些区别?你们是如何使用的?

相关文章

  • iOS多线程 NSOperation

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程 pthread、NSThread

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程: GCD

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程运用

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程基础

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • 多线程介绍

    一、进程与线程 进程介绍 线程介绍 线程的串行 二、多线程 多线程介绍 多线程原理 多线程的优缺点 多线程优点: ...

  • iOS进阶之多线程管理(GCD、RunLoop、pthread、

    深入理解RunLoopiOS多线程--彻底学会多线程之『GCD』iOS多线程--彻底学会多线程之『pthread、...

  • iOS多线程相关面试题

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • 多线程之--NSOperation

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • iOS多线程之--NSThread

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

网友评论

      本文标题:多线程

      本文链接:https://www.haomeiwen.com/subject/hebjuftx.html