并发高级知识
1.synchronized void f()
2.使用Lock,更好的管理异常;尝试获取锁一段时间;
private Lock lock = new ReentrantLock();
public int next() {
lock.lock();
try {
++currentEvenValue;
Thread.yield(); // Cause failure faster
++currentEvenValue;
return currentEvenValue;
} finally {
lock.unlock();
}
尝试获取锁最终未获取锁:
captured = lock.tryLock(2, TimeUnit.SECONDS);
更细粒度的控制;
3.volitail保证了可视性(尤其在多处理器系统)
4.原子性变量类:AtomicInteger,AtomicLong,AtomicReference
重要方法:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
这是一个本地方法:
public final native boolean compareAndSwapInt(Object obj, long l, int i, int j);
在硬件层面保证了原子性
5.临界区,也被称为同步代码块
synchronized(this) {
p.incrementX();
p.incrementY();
temp = getPair();
}
优点:相比同步方法,控制粒度更小,其他线程能更多地访问
注意:synchronized块必须给定一个在其上进行同步的对象,最合理的方式是使用其方法正在被调用的当前对象,如果获得了synchronized块的锁,那么该对象其他的synchronized方法和临界区就不能被调用了
6,线程本地存储:ThreadLocal
private static ThreadLocal<Integer> value =
new ThreadLocal<Integer>() {
private Random rand = new Random(47);
protected synchronized Integer initialValue() {
return rand.nextInt(10000);
}
};
public static void increment() {
value.set(value.get() + 1);
}
注意,ThreadLocal并不是一个Map<Thread,value>,而是在当前Thread线程的一个成员变量:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal.ThreadLocalMap threadLocals = null;
7.终结任务
Thread.interrupt()
Executor.shutdownNow()--interrupte它启动的所有线程
中断某一个单一任务:
Future<?> f = exec.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
print("Interrupting " + r.getClass().getName());
f.cancel(true); // Interrupts if running
8.线程之间的协作:某些部分必须在其他部分被解决之前解决
wait();
notifyAll();唤醒等待这个锁的所有任务
notify();只有一个等待这个锁的任务会被唤醒
上面这三个方法都是Object类中的方法
9.生产者-消费者与队列
wait与notify可以进行生产者-消费者的模型构建,以一种低级的方式解决了任务互操作的问题,即每次交互时都握手。
更高的抽象级别,是使用同步队列来解决任务协作问题,同步队列在任何时刻都只允许一个任务插入或移除元素。
java.util.concurrent.BlockingQueue接口中提供了这个队列,通常可以使用无界队列LinkedBlockingQueue(通过使用ReentrantLock加锁),还可以使用ArrayBlockingQueue,它具有固定的尺寸。
当队列为空时,可以自动挂起消费者任务,当队列中有元素时,可以自动恢复消费者任务,比起wait和notity,这种方式简单而可靠(消除了类之间的耦合)。
一个烤面包的示例:
class Toast {
public enum Status { DRY, BUTTERED, JAMMED }
private Status status = Status.DRY;
private final int id;
public Toast(int idn) { id = idn; }
public void butter() { status = Status.BUTTERED; }
public void jam() { status = Status.JAMMED; }
public Status getStatus() { return status; }
public int getId() { return id; }
public String toString() {
return "Toast " + id + ": " + status;
}
}
class ToastQueue extends LinkedBlockingQueue<Toast> {}
class Toaster implements Runnable {
private ToastQueue toastQueue;
private int count = 0;
private Random rand = new Random(47);
public Toaster(ToastQueue tq) { toastQueue = tq; }
public void run() {
try {
while(!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(
100 + rand.nextInt(500));
// Make toast
Toast t = new Toast(count++);
print(t);
// Insert into queue
toastQueue.put(t);
}
} catch(InterruptedException e) {
print("Toaster interrupted");
}
print("Toaster off");
}
}
// Apply butter to toast:
class Butterer implements Runnable {
private ToastQueue dryQueue, butteredQueue;
public Butterer(ToastQueue dry, ToastQueue buttered) {
dryQueue = dry;
butteredQueue = buttered;
}
public void run() {
try {
while(!Thread.interrupted()) {
// Blocks until next piece of toast is available:
Toast t = dryQueue.take();
t.butter();
print(t);
butteredQueue.put(t);
}
} catch(InterruptedException e) {
print("Butterer interrupted");
}
print("Butterer off");
}
}
// Apply jam to buttered toast:
class Jammer implements Runnable {
private ToastQueue butteredQueue, finishedQueue;
public Jammer(ToastQueue buttered, ToastQueue finished) {
butteredQueue = buttered;
finishedQueue = finished;
}
public void run() {
try {
while(!Thread.interrupted()) {
// Blocks until next piece of toast is available:
Toast t = butteredQueue.take();
t.jam();
print(t);
finishedQueue.put(t);
}
} catch(InterruptedException e) {
print("Jammer interrupted");
}
print("Jammer off");
}
}
// Consume the toast:
class Eater implements Runnable {
private ToastQueue finishedQueue;
private int counter = 0;
public Eater(ToastQueue finished) {
finishedQueue = finished;
}
public void run() {
try {
while(!Thread.interrupted()) {
// Blocks until next piece of toast is available:
Toast t = finishedQueue.take();
// Verify that the toast is coming in order,
// and that all pieces are getting jammed:
if(t.getId() != counter++ ||
t.getStatus() != Toast.Status.JAMMED) {
print(">>>> Error: " + t);
System.exit(1);
} else
print("Chomp! " + t);
}
} catch(InterruptedException e) {
print("Eater interrupted");
}
print("Eater off");
}
}
public class ToastOMatic {
public static void main(String[] args) throws Exception {
ToastQueue dryQueue = new ToastQueue(),
butteredQueue = new ToastQueue(),
finishedQueue = new ToastQueue();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Toaster(dryQueue));
exec.execute(new Butterer(dryQueue, butteredQueue));
exec.execute(new Jammer(butteredQueue, finishedQueue));
exec.execute(new Eater(finishedQueue));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}
10.死锁
必须同时满足4个条件:
1.互斥条件:至少有一个资源不能共享
2.至少有一个任务持有一个资源且等待别的资源
3.资源不能被任务抢占
4.必须有循环等待
防止死锁,只需破坏其中一个即可,最容易的方法是破坏第四个条件
11.CountDownLatch
同步多个任务,CountDownLatch设置一个初始计数值,任务调用countDown()减小计数值,直到为0
12.CyclicBarrier
创建一组任务,当这组任务都完成时才能进行下一步
13.DelayQueue
是一种无界阻塞队列,其中的元素只有在到期时才能被取走
14.PriorityBlockingQueue
优先级队列
15.免锁容器
CopyOnWriteArrayList:对容器的修改和读取可以同时发生
存储数组:
private volatile transient Object[] array;//volatile保证可见性
add方法:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();//使用锁
try {
Object[] elements = getArray();//获取旧的数据的副本
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);//更新数据
return true;
} finally {
lock.unlock();
}
}
CopyOnWriteArraySet:借助CopyOnWriteArrayList实现免锁
ConcurrentHashMap<K, V>:用桶粒度的锁,避免了put和get中对整个map的锁定,尤其在get中,只对一个HashEntry做锁定操作
ConcurrentLinkedQueue
16.乐观加锁:即原子类中的compareAndSet()方法
17.ReadWriteLock:对经常读取的操作进行了优化,但是修改期间仍不能读取