使用ReentrantLock类
java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,而ReentrantLock也能达到同样的效果,并且比synchronized更加的灵活
图书馆有一台电脑,学生们可以通过电脑查询想要的图书在哪个书架上
代码4-1
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Computer {
private Lock lock = new ReentrantLock();
public void select() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "查阅图书");
System.out.println(Thread.currentThread().getName() + "查阅完毕");
lock.unlock();
}
}
class Student extends Thread {
private Computer computer;
public Student(String name, Computer computer) {
super(name);
this.computer = computer;
}
public void run() {
computer.select();
}
}
public class Library {
public static void main(String[] args) {
Computer computer = new Computer();
for (int i = 0; i < 6; i++) {
new Student("学生" + (i + 1) + "号", computer).start();
}
}
}
代码4-1运行结果:
学生2号查阅图书
学生2号查阅完毕
学生6号查阅图书
学生6号查阅完毕
学生1号查阅图书
学生1号查阅完毕
学生3号查阅图书
学生3号查阅完毕
学生4号查阅图书
学生4号查阅完毕
学生5号查阅图书
学生5号查阅完毕
从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印,但线程之间打印的顺序是随机的
使用Condition实现等待/通知
关键字synchronized与wait()、notify()、notifyAll()方法相结合实现等待/通知模式,ReentrantLock类也可以实现同样的功能,但需要借助Condition对象,使用Condition可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即监视器对象)实例,线程对象可以注册再指定的Condition中,从而可以有选择性的进行线程通知,相比使用notify()和notifyAll()方法进行通知,被通知的线程却是由JVM随机选择的,提高了灵活度。
synchronized相当于整个Lock对象只有单一的Condition对象,所有线程都注册再它一个对象身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权
模拟一个老师给3个学生发水果的游戏,count为老师所拥有的水果数量,初始数量为2,每当count为0时,若处在分发水果方法的学生线程,会陷入等待状态,如果已经取完水果的学生,还要再取水果,会先查看老师的水果数量是否为0,如果为0则调用添加水果的方法通知老师添加水果,当老师准备好水果,又会通知所有等待的学生线程继续来获取水果
代码4-2
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Teacher {
private Random random = new Random();
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int count = 2;// 老师所拥有的水果数量
public void takeFruits() {
System.out.println(Thread.currentThread().getName() + "申请水果");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// 调用condition.await()方法前必须先获取锁定,否则会报错
lock.lock();
if (count == 0) {
System.out.println(Thread.currentThread().getName() + "等待水果");
condition.await();
}
if (count != 0) {
count--;
System.out.println(Thread.currentThread().getName() + "获得水果");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void addFruits() {
System.out.println("老师检查水果数量");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if (count == 0) {
count += 2;
System.out.println("老师准备水果");
condition.signalAll();
}
lock.unlock();
}
public int getCount() {
return count;
}
}
class Student extends Thread {
private Teacher teacher;
public Student(String name, Teacher teacher) {
super(name);
this.teacher = teacher;
}
@Override
public void run() {
while (true) {
if (teacher.getCount() == 0) {
teacher.addFruits();
} else {
teacher.takeFruits();
}
}
}
}
public class EatFruit {
public static void main(String[] args) {
Teacher teacher = new Teacher();
for (int i = 0; i < 3; i++) {
new Student("学生" + (i + 1) + "号", teacher).start();
}
}
}
代码4-2运行结果:
学生1号申请水果
学生3号申请水果
学生2号申请水果
学生3号获得水果
学生3号申请水果
学生1号获得水果
老师检查水果数量
学生2号等待水果
学生3号等待水果
老师准备水果
学生1号申请水果
学生2号获得水果
学生2号申请水果
学生3号获得水果
老师检查水果数量
学生2号等待水果
学生1号等待水果
…………
Object类中的wait()方法相当于Condition类中的await()方法
Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法
Object类中的notify()方法相当于Condition类中的signal()方法
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法
使用多个Condition实现通知部分线程
修改老师和学生分水果的游戏,现在,学生分男孩和女孩,男生可以从老师那边拿走苹果,女生可以从老师那边拿走香蕉,当老师苹果或香蕉的数量为0的时候,男生或女生必须陷入等待,当老师添加苹果数量时,需要唤醒等待的男孩线程,同理,当老师添加香蕉数量时,也需要唤醒正在等待香蕉的女孩线程
代码4-3
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Teacher {
private Random random = new Random();
private Lock lock = new ReentrantLock();
private Condition appleCondition = lock.newCondition();
private Condition bananaCondition = lock.newCondition();
private int appleCount = 2;// 老师所拥有的苹果数量
private int bananaCount = 1;// 老师所拥有的香蕉数量
public void takeApple() {
System.out.println(Thread.currentThread().getName() + "申请苹果");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// 调用condition.await()方法前必须先获取锁定,否则会报错
lock.lock();
if (appleCount == 0) {
System.out.println(Thread.currentThread().getName() + "等待苹果");
appleCondition.await();
}
if (appleCount != 0) {
appleCount--;
System.out.println(Thread.currentThread().getName() + "获得苹果");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void takeBanana() {
System.out.println(Thread.currentThread().getName() + "申请香蕉");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// 调用condition.await()方法前必须先获取锁定,否则会报错
lock.lock();
if (bananaCount == 0) {
System.out.println(Thread.currentThread().getName() + "等待香蕉");
bananaCondition.await();
}
if (bananaCount != 0) {
bananaCount--;
System.out.println(Thread.currentThread().getName() + "获得香蕉");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void addApple() {
System.out.println("老师检查苹果数量");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if (appleCount == 0) {
appleCount += 2;
System.out.println("老师准备苹果");
appleCondition.signalAll();
}
lock.unlock();
}
public void addBanana() {
System.out.println("老师检查香蕉数量");
try {
Thread.sleep(random.nextInt(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if (bananaCount == 0) {
bananaCount += 1;
System.out.println("老师准备香蕉");
bananaCondition.signalAll();
}
lock.unlock();
}
public int getAppleCount() {
return appleCount;
}
public int getBananaCount() {
return bananaCount;
}
}
class Boy extends Thread {
private Teacher teacher;
public Boy(String name, Teacher teacher) {
super(name);
this.teacher = teacher;
}
@Override
public void run() {
while (true) {
if (teacher.getAppleCount() == 0) {
teacher.addApple();
}
teacher.takeApple();
}
}
}
class Girl extends Thread {
private Teacher teacher;
public Girl(String name, Teacher teacher) {
super(name);
this.teacher = teacher;
}
@Override
public void run() {
while (true) {
if (teacher.getBananaCount() == 0) {
teacher.addBanana();
}
teacher.takeBanana();
}
}
}
public class EatFruits {
public static void main(String[] args) {
Teacher teacher = new Teacher();
for (int i = 0; i < 3; i++) {
new Boy("男孩" + (i + 1) + "号", teacher).start();
new Girl("女孩" + (i + 1) + "号", teacher).start();
}
}
}
代码4-3运行结果:
男孩1号申请苹果
女孩1号申请香蕉
男孩2号申请苹果
女孩2号申请香蕉
男孩3号申请苹果
女孩3号申请香蕉
女孩1号获得香蕉
老师检查香蕉数量
老师准备香蕉
女孩1号申请香蕉
男孩2号获得苹果
男孩2号申请苹果
男孩3号获得苹果
老师检查苹果数量
男孩1号等待苹果
女孩2号获得香蕉
老师检查香蕉数量
女孩3号等待香蕉
老师准备苹果
男孩3号申请苹果
男孩1号获得苹果
…………
锁分为公平锁和不公平锁,公平锁,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO先进先出的原则等待获取锁。非公平锁则是抢占机制,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式
代码4-4
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Monkey extends Thread {
private Food food;
public Monkey(String name, Food food) {
super(name);
this.food = food;
}
@Override
public void run() {
System.out.println(getName() + "等待香蕉");
food.eatBanana();
}
}
class People extends Thread {
private Food food;
public People(String name, Food food) {
super(name);
this.food = food;
}
@Override
public void run() {
System.out.println(getName() + "等待面包");
food.eatBread();
}
}
public class Food {
private static final int NUM = 5;
public static final Lock FAIR = new ReentrantLock(true);
public static final Lock UNFAIR = new ReentrantLock(false);
public void eatBread() {
try {
FAIR.lock();
System.out.println(Thread.currentThread().getName() + "得到面包 ");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
FAIR.unlock();
}
}
public void eatBanana() {
try {
UNFAIR.lock();
System.out.println(Thread.currentThread().getName() + "得到香蕉 ");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
UNFAIR.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Food food = new Food();
People[] peoples = new People[NUM];
Monkey[] monkeys = new Monkey[NUM];
for (int i = 0; i < NUM; i++) {
peoples[i] = new People((i + 1) + "号人", food);
}
for (People p : peoples) {
p.start();
}
Thread.sleep(1000 * 5);
System.out.println();
for (int i = 0; i < NUM; i++) {
monkeys[i] = new Monkey((i + 1) + "号猴子", food);
}
for (Monkey m : monkeys) {
m.start();
}
}
}
代码4-4运行结果:
1号人等待面包
5号人等待面包
4号人等待面包
3号人等待面包
2号人等待面包
1号人得到面包
5号人得到面包
4号人得到面包
3号人得到面包
2号人得到面包
1号猴子等待香蕉
1号猴子得到香蕉
4号猴子等待香蕉
4号猴子得到香蕉
3号猴子等待香蕉
2号猴子等待香蕉
5号猴子等待香蕉
3号猴子得到香蕉
2号猴子得到香蕉
5号猴子得到香蕉
方法 int getHoldCount()的作用是查询当前线程保持此锁定的个数,也是调用lock()方法的次数
代码4-5
import java.util.concurrent.locks.ReentrantLock;
public class HoldCount {
public ReentrantLock lock = new ReentrantLock();
public void mothodA() {
lock.lock();
System.out.println("A方法");
System.out.println("hold count:" + lock.getHoldCount());
mothodB();
lock.unlock();
}
public void mothodB() {
lock.lock();
System.out.println("B方法");
System.out.println("hold count:" + lock.getHoldCount());
lock.unlock();
}
public int getHoldCount() {
return lock.getHoldCount();
}
public static void main(String[] args) {
HoldCount count = new HoldCount();
count.mothodA();
count.mothodB();
System.out.println("hold count:" + count.getHoldCount());
}
}
代码4-5运行结果:
A方法
hold count:1
B方法
hold count:2
B方法
hold count:1
hold count:0
方法 int getQueueLength()的作用是返回正在等待获取此锁定的线程估计数,比如有六个线程,其中一个获得了锁并因为其他原因陷入等待,那么调用getQueueLength()方法后将返回5
代码4-6
import java.util.concurrent.locks.ReentrantLock;
public class QueueLength {
private ReentrantLock lock = new ReentrantLock();
public void sleep() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁");
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public int getQueueLength() {
return lock.getQueueLength();
}
public static void main(String[] args) throws InterruptedException {
final QueueLength queueLength = new QueueLength();
Runnable runnable = new Runnable() {
@Override
public void run() {
queueLength.sleep();
}
};
for (int i = 0; i < 6; i++) {
new Thread(runnable).start();
}
Thread.sleep(100);
System.out.println(queueLength.getQueueLength());
}
}
代码4-6运行结果:
Thread-0获得锁
5
方法 int getWaitQueueLength(Condition condition)的作用是返回等待与此锁相关的给定条件Condition的线程估计数,比如有6个线程,每个线程获取锁后执行await()方法后,调用getWaitQueueLength(Condition condition)返回6
方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件
代码4-7
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionAwait {
private ReentrantLock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "等待conditionA");
conditionA.await();
System.out.println(Thread.currentThread().getName() + "等待结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "等待conditionB");
conditionB.await();
System.out.println(Thread.currentThread().getName() + "等待结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
conditionA.signal();
conditionB.signal();
lock.unlock();
}
public void waitQueueLength() {
lock.lock();
if (lock.getWaitQueueLength(conditionA) != 0) {
System.out.println("有" + lock.getWaitQueueLength(conditionA) + "等待conditionA");
}
if (lock.getWaitQueueLength(conditionB) != 0) {
System.out.println("有" + lock.getWaitQueueLength(conditionB) + "等待conditionB");
}
lock.unlock();
}
public void hasWaiters() {
lock.lock();
System.out.println("是否有线程在等待conditionA:" + lock.hasWaiters(conditionA));
System.out.println("是否有线程在等待conditionB:" + lock.hasWaiters(conditionB));
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
final ConditionAwait await = new ConditionAwait();
Runnable runnableA = new Runnable() {
@Override
public void run() {
await.awaitA();
}
};
Runnable runnableB = new Runnable() {
@Override
public void run() {
await.awaitB();
}
};
for (int i = 0; i < 3; i++) {
new Thread(runnableA).start();
}
for (int i = 0; i < 5; i++) {
new Thread(runnableB).start();
}
Thread.sleep(100);
await.hasWaiters();
System.out.println("——————————线程等待统计————————-—");
await.waitQueueLength();
System.out.println("——————————开始唤醒线程————————-—");
for (int i = 0; i < 5; i++) {
await.waitQueueLength();
Thread.sleep(100);
await.signal();
}
Thread.sleep(100);
await.hasWaiters();
}
}
代码4-7运行结果:
Thread-0等待conditionA
Thread-1等待conditionA
Thread-2等待conditionA
Thread-3等待conditionB
Thread-4等待conditionB
Thread-5等待conditionB
Thread-6等待conditionB
Thread-7等待conditionB
是否有线程在等待conditionA:true
是否有线程在等待conditionB:true
——————————线程等待统计————————-—
有3等待conditionA
有5等待conditionB
——————————开始唤醒线程————————-—
有3等待conditionA
有5等待conditionB
有2等待conditionA
有4等待conditionB
Thread-0等待结束
Thread-3等待结束
有1等待conditionA
有3等待conditionB
Thread-1等待结束
Thread-4等待结束
有2等待conditionB
Thread-2等待结束
Thread-5等待结束
有1等待conditionB
Thread-6等待结束
Thread-7等待结束
是否有线程在等待conditionA:false
是否有线程在等待conditionB:false
方法boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待此锁定
方法boolean hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁定
代码4-8
import java.util.concurrent.locks.ReentrantLock;
public class HasQueueThread {
private ReentrantLock lock = new ReentrantLock();
public void sleep() {
try {
lock.lock();
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public boolean hasQueuedThreads() {
return lock.hasQueuedThreads();
}
public boolean hasQueuedThread(Thread thread) {
return lock.hasQueuedThread(thread);
}
public static void main(String[] args) throws InterruptedException {
final HasQueueThread hasQueueThread = new HasQueueThread();
Runnable runnable = new Runnable() {
@Override
public void run() {
hasQueueThread.sleep();
}
};
Thread[] threads = new Thread[6];
for (int i = 0; i < 6; i++) {
threads[i] = new Thread(runnable);
}
for (Thread thread : threads) {
thread.start();
}
Thread.sleep(100);
System.out.println("是否有线程正在等待锁定:" + hasQueueThread.hasQueuedThreads());
for (Thread thread : threads) {
if (!hasQueueThread.hasQueuedThread(thread)) {
System.out.println(thread.getName() + "取得锁定");
} else {
System.out.println(thread.getName() + "等待锁定");
}
}
}
}
代码4-8运行结果:
是否有线程正在等待锁定:true
Thread-0取得锁定
Thread-1等待锁定
Thread-2等待锁定
Thread-3等待锁定
Thread-4等待锁定
Thread-5等待锁定
方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待此锁相关的condition条件
方法boolean isFair()的作用是判断是否是公平锁
方法boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
boolean isLocked()查询此锁定是否由任意线程保持
代码4-9
import java.util.concurrent.locks.ReentrantLock;
public class ThreadLock {
private ReentrantLock fair = new ReentrantLock(true);
private ReentrantLock unfair = new ReentrantLock(false);// 默认情况下,ReentrantLock使用非公平锁
public void showFair() {
fair.lock();
System.out.println("fair公平锁情况:" + fair.isFair());
fair.unlock();
}
public void showUnFair() {
fair.lock();
System.out.println("unfair公平锁情况:" + unfair.isFair());
fair.unlock();
}
public void isHeldByCurrentThread() {
System.out.println("lock()前," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
fair.lock();
System.out.println("lock()后,unlock()前," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
fair.unlock();
System.out.println("unlock()后," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
}
public void keepLock() {
try {
unfair.lock();
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
unfair.unlock();
}
}
public void isLocked() {
System.out.println("unfair是否保持锁定:" + unfair.isLocked());
}
public static void main(String[] args) throws InterruptedException {
final ThreadLock lock = new ThreadLock();
Runnable runnableA = new Runnable() {
@Override
public void run() {
lock.showFair();
}
};
new Thread(runnableA).start();
Runnable runnableB = new Runnable() {
@Override
public void run() {
lock.showUnFair();
}
};
new Thread(runnableB).start();
Runnable runnableC = new Runnable() {
@Override
public void run() {
lock.isHeldByCurrentThread();
}
};
new Thread(runnableC).start();
Runnable runnableD = new Runnable() {
@Override
public void run() {
lock.keepLock();
}
};
for (int i = 0; i < 3; i++) {
new Thread(runnableD).start();
}
Thread.sleep(100);
lock.isLocked();
System.exit(0);
}
}
代码4-9运行结果:
fair公平锁情况:true
unfair公平锁情况:false
lock()前,Thread-2是否保持此锁定:false
lock()后,unlock()前,Thread-2是否保持此锁定:true
unlock()后,Thread-2是否保持此锁定:false
unfair是否保持锁定:true
方法void lockInterruptibly()作用与void lock()相似,但如果线程执行的时候被中断会抛出异常
代码4-10
import java.util.concurrent.locks.ReentrantLock;
public class LockInterruptibly {
private ReentrantLock lock = new ReentrantLock();
public void threadWait() {
int count = 0;
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获取锁定");
while (count < Integer.MAX_VALUE / 100) {
count++;
Math.random();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(Thread.currentThread().getName() + "中断异常");
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println(Thread.currentThread().getName() + "解除锁定");
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final LockInterruptibly interruptibly = new LockInterruptibly();
Runnable runnable = new Runnable() {
@Override
public void run() {
interruptibly.threadWait();
}
};
new Thread(runnable).start();
Thread.sleep(3000);
Thread thread = new Thread(runnable);
thread.start();
thread.interrupt();
}
}
代码4-10运行结果:
Thread-0获取锁定
Thread-0解除锁定
Thread-1中断异常
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1219)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at com.jerry.ch4.LockInterruptibly.threadWait(LockInterruptibly.java:12)
at com.jerry.ch4.LockInterruptibly$1.run(LockInterruptibly.java:37)
at java.lang.Thread.run(Thread.java:722)
方法boolean tryLock()尝试是否能获得锁 如果不能获得立即返回
方法boolean tryLock(long timeout, TimeUnit unit)在指定时间内尝试是否能获得锁,如果不能获得立即返回
代码4-11
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLock {
private ReentrantLock lock = new ReentrantLock();
public void tryLockA() {
boolean requireLock = false;
if (lock.tryLock()) {
requireLock = true;
System.out.println(Thread.currentThread().getName() + "获得锁定");
} else {
System.out.println(Thread.currentThread().getName() + "没有获得锁定");
}
if (requireLock) {
lock.unlock();
}
}
public void tryLockB() {
boolean requireLock = false;
try {
if (lock.tryLock(2, TimeUnit.SECONDS)) {
requireLock = true;
System.out.println(Thread.currentThread().getName() + "获得锁定");
} else {
System.out.println(Thread.currentThread().getName() + "没有获得锁定");
}
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (requireLock) {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final TryLock lock = new TryLock();
Runnable runnableA = new Runnable() {
@Override
public void run() {
lock.tryLockA();
}
};
new Thread(runnableA).start();
Thread.sleep(100);
new Thread(runnableA).start();
new Thread(runnableA).start();
Thread.sleep(100);
Runnable runnableB = new Runnable() {
@Override
public void run() {
lock.tryLockB();
}
};
new Thread(runnableB).start();
Thread.sleep(3000);
new Thread(runnableB).start();
new Thread(runnableB).start();
}
}
代码4-11运行结果:
Thread-0获得锁定
Thread-1获得锁定
Thread-2获得锁定
Thread-3获得锁定
Thread-3进入finally
Thread-4获得锁定
Thread-4进入finally
Thread-5没有获得锁定
Thread-5进入finally
方法void awaitUninterruptibly()可以让线程陷入等待状态,但执行线程中断方法时该线程不会中断
代码4-12
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class AwaitUninterruptibly {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("await begin");
condition.await();
System.out.println("await end");
} catch (InterruptedException e) {
System.out.println("catch InterruptedException");
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitUninterruptibly() {
lock.lock();
System.out.println("awaitUninterruptibly begin");
condition.awaitUninterruptibly();
System.out.println("awaitUninterruptibly end");
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
final AwaitUninterruptibly uninterruptibly = new AwaitUninterruptibly();
Runnable runnableA = new Runnable() {
@Override
public void run() {
uninterruptibly.await();
}
};
Thread threadA = new Thread(runnableA);
threadA.start();
Thread.sleep(300);
threadA.interrupt();
Thread.sleep(300);
Runnable runnableB = new Runnable() {
@Override
public void run() {
uninterruptibly.awaitUninterruptibly();
}
};
Thread threadB = new Thread(runnableB);
threadB.start();
Thread.sleep(300);
threadB.interrupt();
}
}
代码4-12运行结果:
await begin
catch InterruptedException
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2017)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2052)
at com.jerrych4.AwaitUninterruptibly.await(AwaitUninterruptibly.java:14)
at com.jerrych4.AwaitUninterruptibly$1.run(AwaitUninterruptibly.java:37)
at java.lang.Thread.run(Thread.java:745)
awaitUninterruptibly begin
日期转换工具
代码4-13
import java.text.ParseException;
import java.util.Date;
public class DateUtils {
public static String DateToString(Date date, String toFormatStr) {
try {
return new java.text.SimpleDateFormat(toFormatStr).format(date);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static Date StringToDate(String dateString, String strFormat) {
try {
Date date = new java.text.SimpleDateFormat(strFormat).parse(dateString);
return date;
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
方法boolean awaitUntil(Date deadline)可以使得线程等待,在指定日期唤醒自己,但如果相关条件condition提前唤醒,则不会等到日期到了再唤醒自己
代码4-14
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class AwaitUntil {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitUntil() {
try {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
lock.lock();
System.out.println(Thread.currentThread().getName() + "开始时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
condition.awaitUntil(calendar.getTime());
System.out.println(Thread.currentThread().getName() + "结束时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void singalAll() {
lock.lock();
System.out.println("唤醒开始时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
condition.signalAll();
System.out.println("唤醒结束时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
final AwaitUntil until = new AwaitUntil();
Runnable runnable = new Runnable() {
@Override
public void run() {
until.awaitUntil();
}
};
Thread threadA = new Thread(runnable);
threadA.start();
Thread.sleep(1000 * 15);
Thread threadB = new Thread(runnable);
threadB.start();
Thread.sleep(100);
until.singalAll();
}
}
代码4-14运行结果:
Thread-0开始时间:2016-11-23 20:58:51
Thread-0结束时间:2016-11-23 20:59:01
Thread-1开始时间:2016-11-23 20:59:06
唤醒开始时间:2016-11-23 20:59:06
唤醒结束时间:2016-11-23 20:59:06
Thread-1结束时间:2016-11-23 20:59:06
使用ReentrantReadWriteLock类
类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()后的任务。这样做虽然保证了实例变量的线程安全性,但效率却非常低下所以,使用读写锁ReentrantReadWriteLock类,可以加快运行效率
读写锁表示有两个锁,一个是读操作相关的锁,也称为共享锁,另一个是写相关锁,也叫排它锁。也就是多个线程读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。
代码4-15
import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLock {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "获取读锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "释放读锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
lock.readLock().unlock();
}
}
public void write() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "获取写锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "释放写锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
lock.writeLock().unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final ReadWriteLock lock = new ReadWriteLock();
Runnable runnableA = new Runnable() {
@Override
public void run() {
lock.read();
}
};
System.out.println("——————————————读读共享——————————————");
for (int i = 0; i < 3; i++) {
new Thread(runnableA).start();
}
Thread.sleep(2000);
System.out.println("——————————————读写互斥——————————————");
Runnable runnableB = new Runnable() {
@Override
public void run() {
lock.write();
}
};
for (int i = 0; i < 3; i++) {
new Thread(runnableB).start();
new Thread(runnableA).start();
}
}
}
代码4-15运行结果:
——————————————读读共享——————————————
Thread-2获取读锁时间:2016-11-24 09:49:10
Thread-1获取读锁时间:2016-11-24 09:49:10
Thread-0获取读锁时间:2016-11-24 09:49:10
Thread-0释放读锁时间:2016-11-24 09:49:11
Thread-2释放读锁时间:2016-11-24 09:49:11
Thread-1释放读锁时间:2016-11-24 09:49:11
——————————————读写互斥——————————————
Thread-4获取读锁时间:2016-11-24 09:49:12
Thread-4释放读锁时间:2016-11-24 09:49:13
Thread-3获取写锁时间:2016-11-24 09:49:13
Thread-3释放写锁时间:2016-11-24 09:49:14
Thread-5获取写锁时间:2016-11-24 09:49:14
Thread-5释放写锁时间:2016-11-24 09:49:15
Thread-8获取读锁时间:2016-11-24 09:49:15
Thread-6获取读锁时间:2016-11-24 09:49:15
Thread-6释放读锁时间:2016-11-24 09:49:16
Thread-8释放读锁时间:2016-11-24 09:49:16
Thread-7获取写锁时间:2016-11-24 09:49:16
Thread-7释放写锁时间:2016-11-24 09:49:17