在java程序中,常用的有两种机制来解决多线程并发问题,一种是sychronized方式,通过锁机制,一个线程执行时,让另一个线程等待,是以时间换空间的方式来让多线程串行执行。而另外一种方式就是ThreadLocal方式,通过创建线程局部变量,以空间换时间的方式来让多线程并行执行。两种方式各有优劣,适用于不同的场景,要根据不同的业务场景来进行选择。 在spring的源码中,就使用了ThreadLo
ThreadLocal 是一个本地线程副本变量工具类,在每个线程中都创建了一个 ThreadLocalMap 对象,简单说 ThreadLocal 就是一种以空间换时间的做法,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享。 原理:线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadL
volatile修饰的变量在各个线程的工作内存中不存在一致性的问题(在各个线程工作的内存中,volatile修饰的变量也会存在不一致的情况,但是由于每次使用之前都会先刷新主存中的数据到工作内存,执行引擎看不到不一致的情况,因此可以认为不存在不一致的问题),但是java的运算并非原子性的操作,导致volatile在并发下并非是线程安全的。
对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。 从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详
线程池中的线程是在第一次提交任务submit时创建的 创建线程的方式有继承Thread和实现Runnable,重写run方法,start开始执行,wait等待,sleep休眠,shutdown停止。 (1)newSingleThreadExecutor:单线程池。 顾名思义就是一个池中只有一个线程在运行,该线程永不超时,而且由于是一个线程,当有多个任务需要处理时,会将它们放置到一个无界阻塞队列中逐
// Java线程池的完整构造函数 public ThreadPoolExecutor( int corePoolSize, // 线程池长期维持的最小线程数,即使线程处于Idle状态,也不会回收。 int maximumPoolSize, // 线程数的上限 long keepAliveTime, // 线程最大生命周期。 TimeUnit unit, //时间单位
创建一个阻塞队列来容纳任务,在第一次执行任务时创建足够多的线程,并处理任务,之后每个工作线程自动从任务队列中获取线程,直到任务队列中任务为0为止,此时线程处于等待状态,一旦有工作任务加入任务队列中,即刻唤醒工作线程进行处理,实现线程的可复用性。 线程池一般包括四个基本组成部分: (1)线程池管理器 用于创建线程池,销毁线程池,添加新任务。 (2)工作线程 线程池中线程,可循环执行任务,在没有任务时
(1)Semaphore同步器 特征: 经典的信号量,通过计数器控制对共享资源的访问 Semaphore(int count):创建拥有count个许可证的信号量 acquire()/acquire(int num) : 获取1/num个许可证 release/release(int num) : 释放1/num个许可证 (2)CountDownLatch同步器 特征: 必须发生指定数量的事件后才
synchronized volatile ReenreantLock 使用局部变量实现线程同步
AQS定义两种资源共享方式 (1)Exclusive(独占) 只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁: 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的 (2)Share(共享) 多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLa
(1)都是可重入锁; (2)ReentrantLock内部是实现了Sync,Sync继承于AQS抽象类。Sync有两个实现,一个是公平锁,一个是非公平锁,通过构造函数定义。AQS中维护了一个state来计算重入次数,避免频繁的持有释放操作带来的线程问题。 (3)ReentrantLock只能定义代码块,而Synchronized可以定义方法和代码块; 4、Synchronized是JVM的一个内部
(1)锁消除 所消除就是虚拟机根据一个对象是否真正存在同步情况,若不存在同步情况,则对该对象的访问无需经过加锁解锁的操作。 比如StringBuffer的append方法,因为append方法需要判断对象是否被占用,而如果代码不存在锁竞争,那么这部分的性能消耗是无意义的。于是虚拟机在即时编译的时候就会将上面的代码进行优化,也就是锁消除。 @Override public synchronized
(1)什么是可重入性 一个线程持有锁时,当其他线程尝试获取该锁时,会被阻塞;而这个线程尝试获取自己持有锁时,如果成功说明该锁是可重入的,反之则不可重入。 (2)synchronized是如何实现可重入性 synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter和monitorexit两个字节码指令。每个锁对象内部维护一个计数器,该计数器初始值为0,表示任何线程都可
(1)相似点 它们都是阻塞式的同步,也就是说一个线程获得了对象锁,进入代码块,其它访问该同步块的线程都必须阻塞在同步代码块外面等待,而进行线程阻塞和唤醒的代码是比较高的。 (2)功能区别 Synchronized是java语言的关键字,是原生语法层面的互斥,需要JVM实现;ReentrantLock 是JDK1.5之后提供的API层面的互斥锁,需要lock和unlock()方法配合try/fina
乐观锁认为对一个对象的操作不会引发冲突,所以每次操作都不进行加锁,只是在最后提交更改时验证是否发生冲突,如果冲突则再试一遍,直至成功为止,这个尝试的过程称为自旋。 乐观锁没有加锁,但乐观锁引入了ABA问题,此时一般采用版本号进行控制; 也可能产生自旋次数过多问题,此时并不能提高效率,反而不如直接加锁的效率高; 只能保证一个对象的原子性,可以封装成对象,再进行CAS操作;