Summary
实现方法:
- volatile
- synchronized
- wait()/notifyAll()
- RetrantLock
- LockSupport
- Semaphore
从实现方式上来看,【方法1】和【方法2】都是基于JAVA语言的原生实现。
【方法1】采用的是共享内存的方式,【方法6】采用的是消息传递,都是借线程通信的方式控制并发;【方法2】、【方法3】、【方法4】、【方法5】采用的是加锁形式的并发控制。
- 除了这几种方法外,还有管道、FIFO(同步队列)等实现方式,此略。
注:本质上,该问题是并发控制问题,因此可以采用线程通信的方式解决;而线程通信主要有两种机制:共享内存和消息传递,而消息传递亦可以基于共享内存来间接实现(非严格实现)。即,JAVA的并发采用的是隐式共享内存模型,但是在此基础上,并发包中诸如信号量等消息传递的线程通信机制亦已基于共享内存的方式非严格间接实现。
1. volatile
要注意的是,volatile本身无原子性(如,
a++
),但对单个变量的读写具有原子性(如,a=1
)。
volatile 关键字保证了共享变量的 ”可见性“,及其通过总线锁、内存屏障(解决重排序问题)的方式,在不引起线程上下文切换的状态下(因此比synchronized
效率高),使一个变量总是对所有线程可见。
- 这不是一个高效的解决方式,但是是一个面试官可以接受的回答。这里采用自旋的方式,效率较低。
在本例中的实现代码如下(完整):
public class syn {
private static volatile int order = 0; // 使用 volatile
private static int n = 10;
public static void main(String[] args){
for(int i=0; i<syn.n; i++){
new Thread(new R(i)).start();
}
}
static class R implements Runnable{
private int id;
public R(int id){
this.id = id;}
@Override
public void run() {
while(true){
if(syn.order == this.id){
// 此处靠只读,保证原子性
System.out.println(this.id);
syn.order = (syn.order+1) % syn.n; // 保证了此处的原子性
}
}
}
}
}
也可以选择在共享变量上自旋:
public void run() {
while(true){
while(syn.order != this.id){
} // 自旋
System.out.println(this.id);
syn.order = (syn.order+1) % syn