实战Java高并发程序设计笔记
多线程的团队协作:同步控制
- synchronied的功能扩展:重入锁
- 简单使用,与synchronied相比,重入锁有着显示的操作过程,必须手动指定何时加锁何时释放锁。对逻辑的控制的灵活性远远要比synchronied要好,在退出临界区是必须释放锁,否则其他线程无法访问该资源而堵塞。
public static void main(String args[]) throws InterruptedException {
Runnable run = new MyRun();
Thread thread1 = new Thread(run, "thread 1");
Thread thread2 = new Thread(run, "thread 2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
static class MyRun implements Runnable {
private static final ReentrantLock lock = new ReentrantLock();
private static int count = 0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
count++;
System.out.println(Thread.currentThread().getName() + " i " + count);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
- 中断响应
- 对于synchronied来说,线程程在等待锁要么所得锁继续执行要么保持等待。而重入锁可以随时释放掉已获取锁的线程的锁。可用于解决死锁。以下代码如果将 thread1.interrupt(); 这行代码注释掉,则代码无法执行完成。
public static void main(String arg[]) {
IntLock lock1 = new IntLock(1);
IntLock lock2 = new IntLock(2);
Thread thread1 = new Thread(lock1, "thread 1");
Thread thread2 = new Thread(lock2, "thread 2");
thread1.start();
thread2.start();
thread1.interrupt();
}
static class IntLock implements Runnable {
int i; //控制加锁顺序
private static final ReentrantLock lock1 = new ReentrantLock();
private static final ReentrantLock lock2 = new ReentrantLock();
public IntLock(int i) {
this.i = i;
}
@Override
public void run() {
try {
if (i == 1) {
System.out.println("lock 1");
lock1.lockInterruptibly();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + e);
}
lock2.lockInterruptibly();
} else {
System.out.println("lock 2");
lock2.lockInterruptibly();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + e);
}
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread()) {//判断当前线程是否持有锁
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
System.out.println(Thread.currentThread().getName() + " 退出 ");
}
}
}
- 锁申请等待时间
- tryLock() :线程尝试获取锁,如果锁并未被其他线程占用,返回true,否则返回false
- tryLock(long timeout, TimeUnit unit) :long timeout 表示时长,TimeUnit unit表示时长的单位,线程尝试获取锁,如果在指定timeout时间内没有获取到锁,则立即返回fase,否则返回true
public boolean tryLock()
public boolean tryLock(long timeout, TimeUnit unit)//long timeout 表示时长,TimeUnit unit表示时长的单位,线程尝试获取锁,如果在指定timeout时间内没有获取到锁,则立即返回fase,否则返回true
- 公平锁
- 非公平锁:随机冲等待队列中挑选一个
- 公平锁:特点不会产生饥饿现象,先请求锁的线程先获取锁,(FIFO先入先出)
public ReentrantLock()非公平锁
public ReentrantLock(boolean fair)//传true 则表示申明公平锁,否则申明非公平锁
- 重入锁的好搭档:Condition 条件
- Condition的作用跟Object.wait和Object.notify类似,Condition借口提供部分如下基本方法
public interface Condition {
void await()
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout)
boolean await(long time, TimeUnit unit)
boolean awaitUntil(Date deadline)
void signal();
void signalAll();
}
- await使当前线程等待,同时释放当前锁,其他线程调用signal或者signalAll,线程会重新获得锁并继续执行。或者当前线程被中断,也能跳出等待
- awaitUninterruptibly与await相同,但是不会响应中断
- signal 随机唤醒一个等待中的线程
- signalAll 唤醒所有等待中的线程
- 允许多个线程同事访问:信号量(Semaphore)
- 信号量可指定多个线程同事访问一个资源
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
tryAcquire(long timeout, TimeUnit unit):在一段时间内如果获得了许可返回true否则返回false
release():释放一个许可
- ReadWriteLock 读写锁
- 读读之间不阻塞
- 读阻塞写,写也会阻塞读
- 写写阻塞