1、实现多个线程,有一个线程收尾工作
CyclicBarrier
public class Test5 {
CyclicBarrier bar = new CyclicBarrier(3,() -> System.out.println("大家到齐了,开饭了"));
public static void main(String[] args) {
// 实现多个线程,有一个线程收尾工作
// 三个人一起吃饭,到齐开饭
var t = new Test5();
new Thread(() -> t.eat(6),"李四").start();
new Thread(() -> t.eat(2),"李四").start();
new Thread(() -> t.eat(8),"李四").start();
}
void eat(int t){
System.out.println(Thread.currentThread().getName() + "开始动身...");
try {
TimeUnit.SECONDS.sleep(t);
System.out.println(Thread.currentThread().getName() + "到达。");
bar.await();
// bar.await(6,TimeUnit.SECONDS);
} catch (InterruptedException e){
e.printStackTrace();
} catch (BrokenBarrierException e){
e.printStackTrace();
}
}
}
实现效果:
2、ReentrantLock 公平锁
ReentranLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。
(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。
ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
(2)synchronized可重入,因为枷锁和解锁自动进行,不必担心最后是否释放锁;
ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。
ReentrantLock好像比synchronized关键字没好太多,我们再去看看synchronized所没有的,一个
最主要的就是ReentrantLock还可以实现公平锁机制。什么叫公平锁讷?也就是在锁上等待时间最长
的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。
公平锁
public class ReentrantLockTest3 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
var t = new ReentrantLockTest3();
new Thread(t::m1,"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
new Thread(t::m2,"t2").start();
}
void m1(){
lock.lock();
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(i);
}
lock.unlock();
}
void m2(){
lock.lock();
System.out.println("m2()...");
lock.unlock();
}
}
public class ReentrantLockTest4 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
var t = new ReentrantLockTest4();
new Thread(t::m1,"T1").start();
Thread t2 = new Thread(t::m2,"T2");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
t2.start();
t2.interrupt();
System.out.println(Thread.activeCount());
}
void m1(){
lock.lock();
System.out.println("线程1启动");
try {
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
void m2(){
try {
lock.lockInterruptibly();
System.out.println("线程2终于打断了");
} catch (InterruptedException e){
System.out.println("编程2...");
} finally {
if (lock.tryLock())
lock.unlock();
System.out.println("线程2结束了");
System.exit(0);
}
}
}
public class ReentrantLockTest5 {
Lock lock = new ReentrantLock(true); // 设置为true为公平锁,效率低但公平
public static void main(String[] args) {
var t = new ReentrantLockTest5();
new Thread(t::m,"T1").start();
new Thread(t::m,"T2").start();
new Thread(t::m,"T3").start();
new Thread(t::m,"T4").start();
}
void m(){
for (int i = 0; i <= 20; i++) {
lock.lock();
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e){
e.printStackTrace();
}
lock.unlock();
}
}
}
3、多个线程使用数量有限的资源
Semaphore是一种基于计数的信号量。它可以设定一个阀值,基于此,多个线程竞争获取许可信号,
做自己的申请后归还,超过阀值后,线程申请许可信号将会被阻塞。
Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数
为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。
public class SemaphoreTest1 {
// 定义semaphore实例,设置许可数为10,即停车位为2个
Semaphore semaphore = new Semaphore(2);
public static void main(String[] args) {
SemaphoreTest1 t = new SemaphoreTest1();
new Thread(t::stop,"预A-SY281").start();
new Thread(t::stop,"预B-SY666").start();
new Thread(t::stop,"预C-SY777").start();
new Thread(t::stop,"预D-SY888").start();
new Thread(t::stop,"预E-SY999").start();
new Thread(t::stop,"预F-SY595").start();
new Thread(t::stop,"预G-SY317").start();
}
void stop(){
String t = Thread.currentThread().getName();
try {
System.out.println(t + ":尝试进入停车场...");
// 尝试获取许可,如果没有,则等待
semaphore.acquire();
// 模拟停车
long time = (long) (Math.random() * 50 + 10);
System.out.println(t + ":进入了停车场,停车" + time + "秒...");
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e){
e.printStackTrace();
} finally {
System.out.println(t + ":开始驶离停车场...");
// 释放许可
semaphore.release();
System.out.println(t + ":离开了停车场!");
}
}
}
实现效果:
public class Semaphore implements java.io.Serializable{
// 最多支持N个资源访问
public Semaphore(int permits);
// 获取可用的资源
// 如果可用,信号量内部的资源个数减掉1
// 如果没有可用资源线程会阻塞在该方法中,不能结束该方法,不能返回,直到有可用的资源为止
public void acquire() throws InterruptedException;
// 当前可用的资源个数,permits - availablePermits() = 正在使用的资源个数
public int availablePermits();
// 释放资源,释放后信号量内部的资源个数会增加1
// 如果有被阻塞的线程,释放后会唤醒一个线程去获取资源
// acquire() 和 release() 要成对使用,一半release() 放在finally代码块中
public void release();
}
public class SemaphoreTest2 {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 10; i++) {
new Thread(new MyRunnable(semaphore),"第" + i + "个人").start();
}
}
static class MyRunnable implements Runnable{
private Semaphore semaphore;
public MyRunnable(Semaphore semaphore){
this.semaphore = semaphore;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
int availablePermits = semaphore.availablePermits();
if (availablePermits > 0){
System.out.println(name + "无人,可用");
} else {
System.out.println(name + "有人,请排队。。。\t");
}
try {
// 如果没有拿到资源将一直等待,直到有人释放,拿到资源
semaphore.acquire();
System.out.println(name + "轮到我了");
// 模拟使用时间
Thread.sleep(1000);
System.out.println(name + "使用完毕\t");
} catch (InterruptedException e){
} finally {
// 使用完释放资源
semaphore.release();
}
}
}
}
实现效果: