JDK并发包
同步控制:
重入锁:synchronized的功能扩展
java.util.concurrent.locks.ReentrantLock
ReentrantLock的几个重要方法如下:
lock():获得锁,如果锁已被占用,则等待
lockInterruptibly():获得锁,但优先响应中断
tryLock():尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回
tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁
unlock():释放锁
在重入锁的实现中,主要包含三个要素:
1.原子状态。原子状态使用CAS操作来存储当前锁的状态,判断锁是否已经被别的线程持有
2.等待队列。所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待队列中唤醒一个线程,继续工作
3.阻塞原语park()和unpark(),用来挂起和恢复线程。没有得到锁的线程将会被挂起(有关park()和unpark()方法的介绍,见LockSupport线程阻塞工具类)
重入锁的搭档:Condition条件
Condition接口的基本方法如下:
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
上述方法含义如下:
await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或signalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。和Object.wait()方法相似。
awaitUninterruptibly()方法与await()方法基本相同,但是它不会在等待过程中响应中断。
singal()方法用户唤醒一个在等待中的线程,singnalAll()方法会唤醒所有等待中的线程。
信号量(Semaphore):
信号量是对锁的扩展,为多线程协作提供了更为强大的控制方法。无论是内部锁(synchronized)还是重入锁(ReentrantLock),一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。
信号量主要提供了以下构造函数:
public Semaphore(int permits)
public Semaphore(int permits, boolean fair) // 第二个参数可以指定是否公平
在构造信号量对象时,必须要指定信号量作为准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,就相当于指定了同时有多少个线程可以访问某一个资源。
信号量的主要方法:
public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()
acquire()方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。acquireUninterruptibly()方法与acquire()类似,但不响应中断。
tryAcquire()尝试获得一个许可,成功则返回true,失败则返回false,它不会等待,会立即返回。release()释放一个许可给其它线程。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemophoreDemo implements Runnable {
final Semaphore sema = new Semaphore(5);
@Override
public void run() {
try {
sema.acquire();
//模拟耗时操作
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId()+":done!");
sema.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(20);
final SemophoreDemo demo = new SemophoreDemo();
for(int i=0; i<20; i++){
exec.submit(demo);
}
exec.shutdown();
}
}
ReadWriteLock读写锁:
ReadWriteLock是JDK5中提供的读写分离锁,读写分离可以有效地帮助减少锁竞争,以提升系统新能。如果在系统中,读操作次数远远大于写操作,则读写锁可以很大程度上地提升性能。
示例代码:
package com.canww.test1;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private static Lock lock = new ReentrantLock(); // 普通重入锁
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 读写分离锁
private static Lock readLock = readWriteLock.readLock(); // 读锁
private static Lock writeLock = readWriteLock.writeLock(); // 写锁
private int value;
public Object doRead(Lock lock) throws InterruptedException{
try{
lock.lock(); //模拟读操作
Thread.sleep(1000); //读操作耗时越多,读写锁的优势就越明显
return value;
}finally{
lock.unlock();
}
}
public void doWrite(Lock lock, int index) throws InterruptedException{
try{
lock.lock(); //模拟写操作
Thread.sleep(1000);
value = index;
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
final ReadWriteLockDemo demo = new ReadWriteLockDemo();
Runnable read = new Runnable(){
public void run() {
try {
demo.doRead(readLock); //demo.doRead(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable write = new Runnable(){
public void run() {
try {
demo.doWrite(writeLock, new Random().nextInt()); // demo.doWrite(lock, new Random().nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for(int i=0; i<18; i++){
new Thread(read).start();
}
for(int i=18; i<20; i++){
new Thread(write).start();
}
}
}