本文共 6441 字,大约阅读时间需要 21 分钟。
具体就是指 :java.util.concurrent 的并发包
保证线程安全,和sychronized使用差不所
ReentrantLock():构造方法,参数为true则为公平锁,默认非公平锁
lock():加锁 unlock():释放锁//全局变量 private static int number = 0; //循环次数 private static final int maxSize = 100000; public static void main(String[] args) throws InterruptedException { //1.创建手动锁 Lock lock = new ReentrantLock(); //+10w Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < maxSize; i++) { //2.加锁 lock.lock(); try { number++; }finally { //3.释放锁 lock.unlock(); } } } }); t1.start(); //-10w Thread t2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < maxSize; i++) { lock.lock(); try { number--; }finally { lock.unlock(); } } } }); t2.start(); //等t1,t2线程执行完 t1.join(); t2.join(); System.out.println("运行结果为:" + number); }
最后输出0,线程安全
注意事项:
(1)lock 写在 try 之前 (2)一定要记得在 finally 里面进行 unlock()用来控制锁的数量
Semaphore():构造方法,可以设置信号量个数,以及是否为公平锁
acquire() :尝试获取锁,如果可以正常获取到,则执行后面的业务逻辑,如果获取失败,则阻塞等待 release() :释放锁我们构造4辆车,以及2个停车位,让4辆车进入2个停车位,用到Semaphore信号量
public static void main(String[] args) { //创建信号量 Semaphore semaphore = new Semaphore(2); //线程池执行一个任务相当于一辆车进入停车场 ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100)); for (int i = 0; i < 4; i++) { //创建任务1 executor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " 到达停车场"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //试图进入车位 try { //尝试获取锁 semaphore.acquire(); //当代码执行到此处说明已经获取到了锁 System.out.println(Thread.currentThread().getName() + " 进入车位----"); //车辆停留的时间构建 int num = 1 + new Random().nextInt(5); try { Thread.sleep(num * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "离开停车场。。。。"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放锁 semaphore.release(); } } }); } }可以看到,始终只有2辆车占用停车位,哪个车走了,停车位让出来了,后面的车才能进入
从而就实现了流量控制
计数器是用来保证一组线程同时完成某个任务
CountDownLatch():构造方法,参数为n个线程
await() :等待,当线程数量不满足 CountDownLatch 的数量的时候,执行此代码会阻塞等待,直到数量满足之后,再执行 await 之后的代码 countDown():计数器数量-1在 CountDownLatch 里面有一个计数器,每次调用countDown() 方法的时候,计数器数量-1,直到减到 0 之后,就可以执行 await() 之后的代码了
设置5个人同时起跑,每到达终点一个人,计数器-1,最后计数器为0时,公布成绩
public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(5); for (int i = 1; i < 6; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始起跑"); try { Thread.sleep(finalI * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "到达终点"); //计数器-1 latch.countDown(); } }).start(); } //阻塞等待 latch.await(); System.out.println("所有人都到达了终点,公布排名"); }由此可见,当计数器没到0之前,就会阻塞等待,到0后,则执行await后面的代码
CountDownLatch 计时器的使用是一次性的,当用完一次后,就不能再使用了
也是计时器,不过这个可以解决CountDownLatch的缺点,可以多次进行计数
CyclicBarrier(int,Runnable):在int线程计数器为0时,继续执行runnable方法
await():等待,(1)计数器 -1 (2)判断计数器是否为 0,如果为 0 执行之后的代码,如果不为 0 阻塞等待 PS:当计数器为 0 时,首先会执行 await 之后的代码,将计数器重置和上面计数器例子一样,不过这时候10个线程起跑,需要每5个人记录一下时间
public static void main(String[] args) throws InterruptedException { CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { @Override public void run() { System.out.println("执行了CyclicBarrier里面的Runnable"); } }); for (int i = 1; i < 11; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始起跑"); try { Thread.sleep(1000 * finalI); } catch (InterruptedException e) { e.printStackTrace(); } // try { System.out.println(Thread.currentThread().getName() + "等待其他人---"); //计数器-1,判断计数器是否为0 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } // 代码执行到此行,说明已经有一组线程满足条件了 System.out.println(Thread.currentThread().getName() + "执行结束。。。"); } }).start(); } }可以看到此类可以循环的计数,每次计数器为0时,又将计数器置为初始值
转载地址:http://pixvi.baihongyu.com/