Java并发编程相关面试题(二)

1. Java 中垃圾回收有什么目的?什么时候进行垃圾回收?
垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行的。
垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。
2. 线程之间如何通信及线程之间如何同步
在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步。通信是指线
程之间以如何来交换信息。一般线程之间的通信机制有两种:共享内存和消息传递。
Java 的并发采用的是共享内存模型, Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。如果编写多线程程序的 Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。
3. Java 内存模型
共享内存模型指的就是 Java 内存模型 ( 简称 JMM) JMM 决定一个线程对共享变量的写入时 ,能对另一个线程可见。从抽象的角度来看, JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存( main memory )中,每个线程都有一个私有的本地内存( local memory ),本
地内存中存储了该线程以读 / 写共享变量的副本。本地内存是 JMM 的一个抽象概念,并不真实存
在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
从上图来看,线程 A 与线程 B 之间如要通信的话,必须要经历下面 2 个步骤:
1. 首先,线程 A 把本地内存 A 中更新过的共享变量刷新到主内存中去。
2. 然后,线程 B 到主内存中去读取线程 A 之前已更新过的共享变量。
下面通过示意图来说明线程之间的通信
总结:什么是 Java 内存模型: java 内存模型简称 jmm ,定义了一个线程对另一个线程可见。共享变
量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本
地内存没有及时刷新到主内存,所以就会发生线程安全问题。
4. 如果对象的引用被置为 null ,垃圾收集器是否会立即释放对象占用的内存?
不会,在下一个垃圾回调周期中,这个对象将是被可回收的。
也就是说并不会立即被垃圾收集器立刻回收,而是在下一次垃圾回收时才会释放其占用的内存。
5. finalize() 方法什么时候被调用?析构函数 (finalization) 的目的是什么?
1. 垃圾回收器( garbage colector )决定回收某对象时,就会运行该对象的 finalize() 方法;
finalize Object 类的一个方法,该方法在 Object 类中的声明 protected void finalize() throws
Throwable { } 在垃圾回收器执行时会调用被回收对象的 finalize() 方法,可以覆盖此方法来实现对
其资源的回收。注意:一旦垃圾回收器准备释放对象占用的内存,将首先调用该对象的 finalize()
法,并且下一次垃圾回收动作发生时,才真正回收对象占用的内存空间
1. GC 本来就是内存回收了,应用还需要在 finalization 做什么呢? 答案是大部分时候,什么都
不用做 ( 也就是不需要重载 ) 。只有在某些很特殊的情况下,比如你调用了一些 native 的方法
( 一般是 C 写的 ) ,可以要在 finaliztion 里去调用 C 的释放函数。
Finalizetion 主要用来释放被对象占用的资源(不是指内存,而是指其他资源,比如文件 (File
Handle) 、端口 (ports) 、数据库连接 (DB Connection) 等)。然而,它不能真正有效地工作。
6. 什么是重排序
程序执行的顺序按照代码的先后顺序执行。
一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,进行重新排序(重排序),
它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和
代码顺序执行的结果是一致的
则因为重排序,他还可能执行顺序为(这里标注的是语句的执行顺序) 2-1-3-4 1-3-2-4 但绝不
可能 2-1-4-3 ,因为这打破了依赖关系。
显然重排序对单线程运行是不会有任何问题,但是多线程就不一定了,所以我们在多线程编程时就
得考虑这个问题了。
7. 重排序实际执行的指令步骤
1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2. 指令级并行的重排序。现代处理器采用了指令级并行技术( ILP )来将多条指令重叠执行。如果不
存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3. 内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在
乱序执行。
这些重排序对于单线程没问题,但是多线程都可能会导致多线程程序出现内存可见性问题。
8. 重排序遵守的规则
as-if-serial
1. 不管怎么排序,结果不能改变
2. 不存在数据依赖的可以被编译器和处理器重排序
3. 一个操作依赖两个操作,这两个操作如果不存在依赖可以重排序
4. 单线程根据此规则不会有问题,但是重排序后多线程会有问题
9. as-if-serial 规则和 happens-before 规则的区别
as-if-serial 语义保证单线程内程序的执行结果不被改变, happens-before 关系保证正确同步的多
线程程序的执行结果不被改变。
as-if-serial 语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行
的。 happens-before 关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多
线程程序是按 happens-before 指定的顺序来执行的。
as-if-serial 语义和 happens-before 这么做的目的,都是为了在不改变程序执行结果的前提下,尽
可能地提高程序执行的并行度。
10. 并发关键字 synchronized
Java 中, synchronized 关键字是用来控制线程同步的,就是在多线程的环境下,控制
synchronized 代码段不被多个线程同时执行。 synchronized 可以修饰类、方法、变量。
另外,在 Java 早期版本中, synchronized 属于重量级锁,效率低下,因为监视器锁( monitor
是依赖于底层的操作系统的 Mutex Lock 来实现的, Java 的线程是映射到操作系统的原生线程之上
的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时
需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这
也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面
synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。 JDK1.6 对锁的
实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来
减少锁操作的开销。
11. 说说自己是怎么使用 synchronized 关键字,在项目中用到了吗
synchronized 关键字最主要的三种使用方式:
修饰实例方法 : 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
修饰静态方法 : 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个
实例对象,是类成员( static 表明这是该类的一个静态资源,不管 new 了多少个对象,只有一
份)。所以如果一个线程 A 调用一个实例对象的非静态 synchronized 方法,而线程 B 需要调用这个
实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态
synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实
例对象锁。
修饰代码块 : 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
总结: synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class
类上锁。 synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String
a) 因为 JVM 中,字符串常量池具有缓存功能!
12. 单例模式了解吗?给我解释一下双重检验锁方式实现单例模式!
双重校验锁实现对象单例(线程安全)
说明:
双锁机制的出现是为了解决前面同步问题和性能问题,看下面的代码,简单分析下确实是解决了多
线程并行进来不会出现重复 new 对象,而且也实现了懒加载
另外,需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。
uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton();
这段代码其实是分为三步执行:
1. uniqueInstance 分配内存空间
2. 初始化 uniqueInstance
3. uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1 - >3 - >2 。指令重排在单线程环境下不会出现问题,
但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 3 ,此时 T2 调用
getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance ,但此时
uniqueInstance 还未被初始化。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
13. 说一下 synchronized 底层实现原理?
Synchronized 的语义底层是通过一个 monitor (监视器锁)的对象来完成,
每个对象有一个监视器锁 (monitor) 。每个 Synchronized 修饰过的代码当它的 monitor 被占用时就
会处于锁定状态并且尝试获取 monitor 的所有权 ,过程:
1 、如果 monitor 的进入数为 0 ,则该线程进入 monitor ,然后将进入数设置为 1 ,该线程即为
monitor 的所有者。
2 、如果线程已经占有该 monitor ,只是重新进入,则进入 monitor 的进入数加 1.
3 、如果其他线程已经占用了 monitor ,则该线程进入阻塞状态,直到 monitor 的进入数为 0 ,再
重新尝试获取 monitor 的所有权。
14. synchronized 可重入的原理
重入锁是指一个线程获取到该锁之后,该线程可以继续获得该锁。底层原理维护一个计数器,当线
程获取该锁时,计数器加一,再次获得该锁时继续加一,释放锁时,计数器减一,当计数器值为 0
时,表明该锁未被任何线程所持有,其它线程可以竞争获取锁。
15. 什么是自旋
很多 synchronized 里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁
可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然
synchronized 里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在 synchronized
的边界做忙循环,这就是自旋。如果做了多次循环发现还没有获得锁,再阻塞,这样可能是一种更
好的策略。
忙循环:就是程序员用循环让一个线程等待,不像传统方法 wait(), sleep() yield() 它们都放弃了
CPU 控制,而忙循环不会放弃 CPU ,它就是在运行一个空循环。这么做的目的是为了保留 CPU
存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避
免重建缓存和减少等待重建的时间就可以使用它了。
16. 多线程中 synchronized 锁升级的原理是什么?
synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候
threadid 为空, jvm 让其持有偏向锁,并将 threadid 设置为其线程 id ,再次进入的时候会先判断
threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为
轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的
对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方
式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访
问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解锁的一些 CAS 操作(比
如等待队列的一些 CAS 操作),这种情况下,就会给线程加一个偏向锁。 如果在运行过程中,遇
到了其他线程抢占锁,则持有偏向锁的线程会被挂起, JVM 会消除它身上的偏向锁,将锁恢复到标
准的轻量级锁。
轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁
争用的时候,轻量级锁就会升级为重量级锁;
重量级锁是 synchronized ,是 Java 虚拟机中最为基础的锁实现。在这种状态下, Java 虚拟机会阻
塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。
17. 线程 B 怎么知道线程 A 修改了变量
1 volatile 修饰变量
2 synchronized 修饰修改变量的方法
3 wait/notify
4 while 轮询
18. 当一个线程进入一个对象的 synchronized 方法 A 之后,其它线程是否可进入
此对象的 synchronized 方法 B
不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的
synchronized 修饰符要求执行方法时要获得对象的锁,如果已经进入 A 方法说明对象锁已经被取
走,那么试图进入 B 方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
19. synchronized volatile CAS 比较
1 synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
2 volatile 提供多线程共享变量可见性和禁止指令重排序优化。
3 CAS 是基于冲突检测的乐观锁(非阻塞)
20. synchronized Lock 有什么区别?
首先 synchronized Java 内置关键字,在 JVM 层面, Lock 是个 Java 类;
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;
lock 需要自己加锁和释放锁,如果使用不当没有 unLock() 去释放锁就会造成死锁。
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
21. synchronized ReentrantLock 区别是什么?
synchronized 是和 if else for while 一样的关键字, ReentrantLock 是类,这是二者的本质
区别。既然 ReentrantLock 是类,那么它就提供了比 synchronized 更多更灵活的特性,可以被继
承、可以有方法、可以有各种各样的类变量
synchronized 早期的实现比较低效,对比 ReentrantLock ,大多数场景性能都相差较大,但是在
Java 6 中对 synchronized 进行了非常多的改进。
相同点:两者都是可重入锁
两者都是可重入锁。 可重入锁 概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对
象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不
可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增 1 ,所以要等到锁的计数器
下降为 0 时才能释放锁。
主要区别如下:
ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
ReentrantLock 只适用于代码块锁,而 synchronized 可以修饰类、方法、变量等。
二者的锁机制其实也是不一样的。 ReentrantLock 底层调用的是 Unsafe park 方法加锁,
synchronized 操作的应该是对象头中 mark word
Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础:
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的 class 对象
同步方法块,锁是括号里面的对象
22. volatile 关键字的作用 对于可见性, Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happens
before 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰
时,它会保证修改的值会立即被更新到主内存中,当有其他线程需要读取时,它会去内存中读取新
值。
从实践角度而言, volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见
java.util.concurrent.atomic 包下的类,比如 AtomicInteger
volatile 常用于多线程环境下的单次操作 ( 单次读或者单次写 )
23. Java 中能创建 volatile 数组吗?
能, Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。意思
是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元
素, volatile 标示符就不能起到之前的保护作用了。
24. volatile 变量和 atomic 变量有什么不同?
volatile 变量可以确保先行关系,即写操作会发生在后续的读操作之前 , 但它并不能保证原子性。
例如用 volatile 修饰 count 变量,那么 count++ 操作就不是原子性的。
AtomicInteger 类提供的 atomic 方法可以让这种操作具有原子性如 getAndIncrement() 方法会
原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。
25. volatile 能使得一个非原子操作变成原子操作吗?
关键字 volatile 的主要作用是使变量在多个线程间可见,但无法保证原子性,对于多个线程访问同
一个实例变量需要加锁进行同步。
虽然 volatile 只能保证可见性不能保证原子性,但用 volatile 修饰 long double 可以保证其操作原子
性。
所以从 Oracle Java Spec 里面可以看到:
对于 64 位的 long double ,如果没有被 volatile 修饰,那么对其操作可以不是原子的。在操作的时
候,可以分成两步,每次对 32 位操作。
如果使用 volatile 修饰 long double ,那么其读写都是原子操作
对于 64 位的引用地址的读写,都是原子操作
在实现 JVM 时,可以自由选择是否把读写 long double 作为原子操作
推荐 JVM 实现为原子操作
26. synchronized volatile 的区别是什么?
synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的
可见性;禁止指令重排序。
区别
volatile 是变量修饰符; synchronized 可以修饰类、方法、变量。
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改
可见性和原子性。
volatile 不会造成线程的阻塞; synchronized 可能会造成线程的阻塞。
volatile 标记的变量不会被编译器优化; synchronized 标记的变量可以被编译器优化。 volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比 synchronized 关键字要好。但是
volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块。 synchronized 关键
字在 JavaSE1.6 之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻
量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场
景还是更多一些。
27. final 不可变对象,它对写并发应用有什么帮助?
不可变对象 (Immutable Objects) 即对象一旦被创建它的状态(对象的数据,也即对象属性值)就
不能改变,反之即为可变对象 (Mutable Objects)
不可变对象的类即为不可变类 (Immutable Class) Java 平台类库中包含许多不可变类,如
String 、基本类型的包装类、 BigInteger BigDecimal 等。
只有满足如下状态,一个对象才是不可变的;
它的状态不能在创建后再被修改;
所有域都是 final 类型;并且,它被正确创建(创建期间没有发生 this 引用的逸出)。
不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。
28. Lock 接口和 synchronized 对比同步它有什么优势?
Lock 接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完
全不同的性质,并且可以支持多个相关类的条件对象。
它的优势有:
1 )可以使锁更公平
2 )可以使线程在等待锁的时候响应中断
3 )可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间
4 )可以在不同的范围,以不同的顺序获取和释放锁
整体上来说 Lock synchronized 的扩展版, Lock 提供了无条件的、可轮询的 (tryLock 方法 ) 、定
时的 (tryLock 带参方法 ) 、可中断的 (lockInterruptibly) 、可多条件队列的 (newCondition 方法 ) 锁操
作。另外 Lock 的实现类基本都支持非公平锁 ( 默认 ) 和公平锁, synchronized 只支持非公平锁,当
然,在大部分情况下,非公平锁是高效的选择。
29. 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候
都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多
这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如 Java 里面的
同步原语 synchronized 关键字的实现也是悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是
在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适
用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实
都是提供的乐观锁。在 Java java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观
锁的一种实现方式 CAS 实现的。
30. 什么是 CAS
CAS compare and swap 的缩写,即我们所说的比较交换。 cas 是一种基于锁的操作,而且是乐观锁。在 java 中锁分为乐观锁和悲观锁。悲观锁是将资源锁
住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态
度,通过某种方式不加锁来处理资源,比如通过给记录加 version 来获取数据,性能较悲观锁有很
大的提高。
CAS 操作包含三个操作数 —— 内存位置( V )、预期原值( A )和新值 (B) 。如果内存地址里面的
值和 A 的值是一样的,那么就将内存里面的值更新成 B CAS 是通过无限循环来获取数据的,若果
在第一轮循环中, a 线程获取地址里面的值被 b 线程修改了,那么 a 线程需要自旋,到下次循环才
有可能机会执行。
java.util.concurrent.atomic 包下的类大多是使用 CAS 操作来实现的
(AtomicInteger,AtomicBoolean,AtomicLong)
31. CAS 的会产生什么问题?
1 ABA 问题:
比如说一个线程 one 从内存位置 V 中取出 A ,这时候另一个线程 two 也从内存中取出 A ,并且 two
行了一些操作变成了 B ,然后 two 又将 V 位置的数据变成 A ,这时候线程 one 进行 CAS 操作发现内存
中仍然是 A ,然后 one 操作成功。尽管线程 one CAS 操作成功,但可能存在潜藏的问题。从
Java1.5 开始 JDK atomic 包里提供了一个类 AtomicStampedReference 来解决 ABA 问题。
2 、循环时间长开销大:
对于资源竞争严重(线程冲突严重)的情况, CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,
效率低于 synchronized
3 、只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量
操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。
32. 什么是原子类
java.util.concurrent.atomic 包:是原子类的小工具包,支持在单个变量上解除锁的线程安全编程
原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 - - 写操作。
比如: AtomicInteger 表示一个 int 类型的值,并提供了 get set 方法,这些 Volatile 类型的 int
变量在读取和写入上有着相同的内存语义。它还提供了一个原子的 compareAndSet 方法(如果该
方法成功执行,那么将实现与读取 / 写入一个 volatile 变量相同的内存效果),以及原子的添加、
递增和递减等方法。 AtomicInteger 表面上非常像一个扩展的 Counter 类,但在发生竞争的情况
下能提供更高的可伸缩性,因为它直接利用了硬件对并发的支持。
简单来说就是原子类来实现 CAS 无锁模式的算法
33. 原子类的常用类
AtomicBoolean
AtomicInteger
AtomicLong
AtomicReference
34. 说一下 Atomic 的原理?
Atomic 包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引
用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线
程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。
35. 死锁与活锁的区别,死锁与饥饿的区别?
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的
现象,若无外力作用,它们都将无法推进下去。
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失
败。
活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,这就是所谓的 , 而处于死锁的
实体表现为等待;活锁有可能自行解开,死锁则不能。
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。
Java 中导致饥饿的原因:
1 、高优先级线程吞噬所有的低优先级线程的 CPU 时间。
2 、线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该
同步块进行访问。
3 、线程在等待一个本身也处于永久等待完成的对象 ( 比如调用这个对象的 wait 方法 ) ,因为其
他线程总是被持续地获得唤醒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值