“【面试:Java并发】”的版本间差异
		
		
		
		
		
		跳到导航
		跳到搜索
		
				
		
		
	
 (创建页面,内容为“category:面试  === sleep() 和 wait()、wait(n) 的区别? === # '''sleep()''': 是 '''Thread''' 类的'''静态方法''',当前线程将睡眠 n 毫秒,线程进入阻塞状态。 #* 当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。 #* sleep 不释放锁(如果有的话); # '''wait()''': 是 '''Object''' 的方法,必须与 '''synchronized''' 关键字一起使用,线程进入阻塞状态。 #*…”)  | 
				无编辑摘要  | 
				||
| (未显示同一用户的1个中间版本) | |||
| 第1行: | 第1行: | ||
[[category:面试]]  | [[category:面试]]  | ||
== sleep() 和 wait()、wait(n) 的区别? ==  | |||
# '''sleep()''': 是 '''Thread''' 类的'''静态方法''',当前线程将睡眠 n 毫秒,线程进入阻塞状态。  | # '''sleep()''': 是 '''Thread''' 类的'''静态方法''',当前线程将睡眠 n 毫秒,线程进入阻塞状态。  | ||
#* 当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。  | #* 当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。  | ||
| 第9行: | 第9行: | ||
#* wait 会释放互斥锁。  | #* wait 会释放互斥锁。  | ||
== synchronized 关键字<ref name="锁"/> ==  | |||
  synchronized 底层实现:【monitor 机制】  |   synchronized 底层实现:【monitor 机制】  | ||
| 第18行: | 第18行: | ||
* 对象锁是一种重量锁(monitor)。  | * 对象锁是一种重量锁(monitor)。  | ||
* 该关键字是一个几种锁的封装。synchronized 的锁机制会根据线程竞争情况在运行时会有'''偏向锁'''(单一线程)、'''轻量锁'''(多个线程访问 synchronized 区域)、'''对象锁'''(重量锁,多个线程存在竞争的情况)、'''自旋锁'''等。  | * 该关键字是一个几种锁的封装。synchronized 的锁机制会根据线程竞争情况在运行时会有'''偏向锁'''(单一线程)、'''轻量锁'''(多个线程访问 synchronized 区域)、'''对象锁'''(重量锁,多个线程存在竞争的情况)、'''自旋锁'''等。  | ||
* synchronized 关键字在“'''原子性、可见性与有序性'''”<ref name="原子性、可见性与有序性"/>这三种特性的时候,都可以作为其中一种的解决方案。  | |||
== volatile 关键字<ref name="锁"/><ref >[[深入理解JVM:Java内存模型与线程#对于volatile型变量的特殊规则]]</ref> ==  | |||
volatile '''保证可见性、有序性,不保证原子性'''<ref name="原子性、可见性与有序性"/>。  | |||
# '''可见性''':主内存和工作内存,直接与主内存产生交互,进行读写操作;  | |||
#* volatile修饰的变量:  | |||
#*# 每次使用前,都必须先从主内存刷新最新的值;  | |||
#*# 每次修改后,都必须立刻同步回主内存中;  | |||
# '''有序性''':禁止 JVM 进行的指令重排序。  | |||
#* volatile修饰的变量:不会被指令重排序优化。  | |||
 可以利用“volatile”来实现“双锁检测”(Double Check Lock,DCL)的单例模式。  | |||
=== volatile 能使得一个非原子操作变成原子操作吗? ===  | |||
<big>'''能'''</big>,一个典型的例子是在类中有一个 long / double 类型的成员变量:  | |||
: 对于类中有一个 long 类型的成员变量,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。  | |||
 “long和double的非原子性协定”<ref >[[深入理解JVM:Java内存模型与线程#针对long和double型变量的特殊规则]]</ref>:  | |||
    允许虚拟机(32 位的 JVM才可能)将没有被 volatile 修饰的 64 位数据(long 和 double)的读写操作划分为两次 32 位的操作来进行。  | |||
=== volatile 修饰符的有过什么实践? ===  | |||
# 原子读写(64 位数据):如上所述。  | |||
# 内存屏障(memory barrier):简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。  | |||
#: 意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。  | |||
== 线程池 ==  | |||
=== ThreadLocal(线程局部变量)关键字? ===  | |||
== 参考 ==  | |||
<references>  | |||
<ref name="锁">Java的锁总结:[[锁]]</ref>  | |||
<ref name="原子性、可见性与有序性">关于Java的“原子性、可见性与有序性”:[[深入理解JVM:Java内存模型与线程#原子性、可见性与有序性]]</ref>  | |||
</references>  | |||
2022年4月1日 (五) 02:22的最新版本
sleep() 和 wait()、wait(n) 的区别?
- sleep(): 是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。
- 当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。
 - sleep 不释放锁(如果有的话);
 
 - wait(): 是 Object 的方法,必须与 synchronized 关键字一起使用,线程进入阻塞状态。
- 当 notify 或者 notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。
 - wait 会释放互斥锁。
 
 
synchronized 关键字[1]
synchronized 底层实现:【monitor 机制】 进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1; 当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。
Synchronized 是在加锁,加对象锁。
- 对象锁是一种重量锁(monitor)。
 - 该关键字是一个几种锁的封装。synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。
 - synchronized 关键字在“原子性、可见性与有序性”[2]这三种特性的时候,都可以作为其中一种的解决方案。
 
volatile 关键字[1][3]
volatile 保证可见性、有序性,不保证原子性[2]。
- 可见性:主内存和工作内存,直接与主内存产生交互,进行读写操作;
- volatile修饰的变量:
- 每次使用前,都必须先从主内存刷新最新的值;
 - 每次修改后,都必须立刻同步回主内存中;
 
 
 - volatile修饰的变量:
 - 有序性:禁止 JVM 进行的指令重排序。
- volatile修饰的变量:不会被指令重排序优化。
 
 
可以利用“volatile”来实现“双锁检测”(Double Check Lock,DCL)的单例模式。
volatile 能使得一个非原子操作变成原子操作吗?
能,一个典型的例子是在类中有一个 long / double 类型的成员变量:
- 对于类中有一个 long 类型的成员变量,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。
 
“long和double的非原子性协定”[4]: 允许虚拟机(32 位的 JVM才可能)将没有被 volatile 修饰的 64 位数据(long 和 double)的读写操作划分为两次 32 位的操作来进行。
volatile 修饰符的有过什么实践?
- 原子读写(64 位数据):如上所述。
 - 内存屏障(memory barrier):简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。
- 意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
 
 
线程池
ThreadLocal(线程局部变量)关键字?
参考
- ↑ 1.0 1.1 Java的锁总结:锁
 - ↑ 2.0 2.1 关于Java的“原子性、可见性与有序性”:深入理解JVM:Java内存模型与线程#原子性、可见性与有序性
 - ↑ 深入理解JVM:Java内存模型与线程#对于volatile型变量的特殊规则
 - ↑ 深入理解JVM:Java内存模型与线程#针对long和double型变量的特殊规则