JUC并发编程
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了JUC并发编程,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含23827字,纯文字阅读大概需要35分钟。
内容图文
![JUC并发编程](/upload/InfoBanner/zyjiaocheng/598/8591492717a34c5a83c324288aa6a844.jpg)
JUC并发编程
- java.util.concurrent
- java.util.concurrent.atomic
- java.util.concurrent.locks
java真得可以开启多线程吗?
不可以
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// native是本地方法,底层是c++,java不能直接操作硬件
private native void start0();
并发和并行
并发:多线程操作同一个资源
- 单核CPU模拟出多条线程,快速交替,实际上是产生的假象。
并行
- CPU多核,多个线程可以同时执行;线程池。
线程有几种状态?
源码
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
// 新建状态(线程一旦被创建就进入了新建状态)
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
// 可运行状态(调用了start方法之后就变成可运行状态)
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
// 阻塞状态(线程因为某种原因放弃了CPU的权利),暂时停止运行,直到线程进入可运行(runnable)状
/// 态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
// 一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
// (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
// (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
// 等待
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
// 超时等待
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
// 终止
TERMINATED;
}
1、wait和sleep的区别
- 他们来自不同的类,wait来自Object类,sleep来自Thread类
// 一般使用TimeUnit方式让线程休眠
TimeUnit.HOURS.sleep(11);
2、关于锁的释放
1、wait会释放锁,sleep不会释放锁。
3、使用的范围是不同的
- wait必须使用在同步代码块中不需要捕获异常
- sleep必须要捕获异常。
LOCK锁
传统Synchribuzed
package com.xiaohei.synchronizedTest;
public class SynchronizedTest {
public static void main(String[] args) {
SendTickets tickets = new SendTickets();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"C").start();
}
}
// 模拟卖票
class SendTickets {
private static int TICKET_NUMBERS = 30;
public synchronized void sale() {
if (TICKET_NUMBERS > 0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(TICKET_NUMBERS--)+"票,剩余"+TICKET_NUMBERS);
}
}
}
LOCK锁
三种实现类:ReentrantLock 可重入锁, ReentrantReadWriteLock.ReadLock 读锁, ReentrantReadWriteLock.WriteLock 写锁
new ReentrantLock() 默认实现为非公平锁。new ReentrantLock(true)实现公平锁
1、公平锁能保证:老的线程排队使用锁,新线程仍然排队使用锁。
2、非公平锁保证:老的线程排队使用锁;但是无法保证新线程抢占已经在排队的线程的锁。
// 非公平锁 默认
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
// 公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
package com.xiaohei.locktTest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
Ticket tickets = new Ticket();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
tickets.sale();
}
},"C").start();
}
}
// 模拟卖票
/**
* lock三部曲
* 1、new ReentrantLock();
* 2、 lock.lock() // 加锁
* 3.lock.unlock() 解锁
* */
class Ticket {
private int TICKET_NUMBERS = 30;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock(); // 枷锁
lock.tryLock(); // 尝试获取锁
try {
if (TICKET_NUMBERS > 0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(TICKET_NUMBERS--)+"票,剩余"+TICKET_NUMBERS);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
synchronized和Lock的区别
- Synchronized内置的Java关键字,Lock是一个Java类
- Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized会自动释放锁,lock必须要手动释放锁,如果不释放,会造成死锁
- Synchronzied 线程1(获得锁,阻塞),线程2(一直等); Lock锁不一定会一直等待下去(lock,tryLock() // 尝试获取锁)
- Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
- Synchronized适合锁少量代码同步问题,Lock适合锁大量的同步代码;
生产者和消费者问题
面试问题:
- 单例模式
- 8种排序算法
- 生产者消费者问题
- 死锁问题
生产者消费者问题
// 两个线程米有问题,如果在加两个线程C和D呢
package com.xiaohei.pc;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data {
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
if (number!=0){
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if (number==0){
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
this.notifyAll();
}
}
// 使用While代替If防止虚假唤醒
package com.xiaohei.pc;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data {
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
while (number!=0){
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
while (number==0){
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
this.notifyAll();
}
}
JUC生产者和消费者问题(LOCK)
与synchnoized的却别:synchronized是使用wait和notify,Lock锁通过lock创建Condition对象,通过condition对象调用await方法和singal方法等待和唤醒。
Condition优势: 实现的精准通知唤醒线程
if换成while条件判断,在多线程条件下会产生虚假唤醒的情况。
package com.xiaohei.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// +1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0){
// 等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
// -1
public synchronized void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知可以消费
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
实现顺序执行任务链
Condition优势: 实现的精准通知唤醒线程
package com.xiaohei.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int flag = 1;
public void printA(){
lock.lock();
try {
while (flag != 1){
// 等待
condition1.await();
}
flag = 2;
condition2.signal();
System.out.println(Thread.currentThread().getName()+"AAAA=>>>AAAAAA");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (flag != 2){
// 等待
condition2.await();
}
flag = 3;
condition3.signal();
System.out.println(Thread.currentThread().getName()+"BBB=>>>BBBBB");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (flag != 3){
// 等待
condition3. ();
}
flag = 1;
condition1.signal();
System.out.println(Thread.currentThread().getName()+"BBB=>>>CCCC");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
什么是锁,如何判断锁是谁?8锁现象
锁只会锁对象还有Class
synchronized锁的是方法调用者
直接上代码
两个线程是先打印打电话还是先打印发短信?
- 第1锁:锁的是同一个Phone对象,
个人理解:因为锁的是当前Phone对象,程序运行开始,首先是A线程拿到方法执行权,然后进入阻塞状态,线程B进入可运行状态调用callPhone()方法,但是由于锁住了同一个对象,A不释放锁,B是无法执行,所以是先调用sendEmail()在调用callPhone()方法。两个方法是同一个锁,谁先拿到谁先执行。
此时有个疑问?调用Sleep()方法,睡眠结束后不是不释放锁吗,为什么B方法还能获取到锁?是因为锁的不是Phone对象吗?那Sleep锁的是谁呢?
package com.xiaohei.lock8;
import java.util.concurrent.TimeUnit;
// 1、发短信 2、打电话
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.callPhone();
},"B").start();
}
}
class Phone{
// 都是同一个锁,谁先拿到谁执行。
public synchronized void sendEmail(){
System.out.println("发短信");
}
public synchronized void callPhone(){
System.out.println("打电话");
}
}
第2个锁
package com.xiaohei.lock8;
import java.util.concurrent.TimeUnit;
// 线程A调用打电话同步方法,线程B调用普通不同步方法,先输出什么?
// 1、biubiubiu 2、发短信
public class Lock2 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sendEmail();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 调用biu方法
new Thread(()->{
phone.biu();
},"B").start();
}
}
class Phone2{
public synchronized void sendEmail(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void callPhone(){
System.out.println("打电话");
}
// **没有加锁,不是同步方法,不受锁的影响,不需要A执行完在执行B**
public void biu(){
System.out.println("biubiubiu");
}
}
第3个锁
两个对象,两个同步方法, 打电话方法加了延时为了结果明贤
package com.xiaohei.lock8;
import java.util.concurrent.TimeUnit;
// 结果:先输出打电话,在输出发短信.
// 因为是两个对象,synchronized锁的是调用方,此时是两把锁,锁不一样,结果按照时间来。没有延时就不一定的。
public class Test1 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone1.sendEmail();
},"A").start();
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.callPhone();
},"B").start();
}
}
class Phone{
public synchronized void sendEmail(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void callPhone(){
System.out.println("打电话");
}
public void zz(){
System.out.println("zzzz");
}
}
第4锁
增加了两个静态同步方法
static 静态方法,类一加载就出现。锁的是 Class 模板
一个类只有一个Class对象
测试案例: 两个对象
package com.xiaohei.lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
// 两个对象只有一个Class,谁先拿到谁先执行
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone1.sendEmail();
},"A").start();
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.callPhone();
},"B").start();
}
}
// 两个对象,两个调用者,锁的是Class对象,因为两个对象来自同一个Class所以谁先拿到谁先执行
class Phone{
public static synchronized void sendEmail(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void callPhone(){
System.out.println("打电话");
}
public void zz(){
System.out.println("zzzz");
}
}
第5锁
一个静态同步方法,一个普通同步方法,一个对象
结果:先输出打电话,在输出发短信啊,因为一个是Class锁,一个是调用者锁(对象锁),是两个锁
package com.xiaohei.lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone1 = new Phone();
// Phone phone2 = new Phone();
new Thread(()->{
phone1.sendEmail();
},"A").start();
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.callPhone();
},"B").start();
}
}
class Phone{
// static锁,锁的是Class
public static void sendEmail(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
// 锁的是调用者 不是一个锁
public synchronized void callPhone(){
System.out.println("打电话");
}
}
总结
- new this对象 调用者
- Class 类模板锁 ,只有一个
多线程下集合类不安全类
在并发下ArrayList是不安全的
解决方案:
1、使用List list = new Vector<>();线程安全
2、使用Collections集合的同步方法, List list = Collections.synchronizedList(new ArrayList<>());
3、并发包下: CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
Vector和CopyOnWriteArrayList区别: Vector底层实现synchronized关键字效率低,CopyOnWriteArrayList使用Lock锁
CopyOnWriteArrayList<>();底层实现
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
HashSet的底层是什么?
// 创建HashSet
Set<Object> hashSet = new HashSet<>();
// 底层实际上是通过创建HshMap实现
public HashSet() {
map = new HashMap<>();
}
// 添加元素 add方法
// 直接把元素通过map put进去,实际上就是存的map的key,因为map的key是不可重复的,所以set也是不可重复的。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// PRESENT参数,是一个固定不变的对象,不可修改。
private static final Object PRESENT = new Object();
Callable接口(多线程创建方式)
- 会抛出异常
- 有返回值
- 调用方法为call()方法
实现
package com.xiaohei.callableTest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Test test = new Test();
FutureTask<String> futureTask = new FutureTask<>(test); // 适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); // 结果会被缓存 提高效率
// 返回值
// V - 此 FutureTask 的 get 方法所返回的结果类型。
String result = futureTask.get(); // 可能会产生阻塞,因为需要等线程查询结束
System.out.println(result);
}
}
class Test implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("Call");
return "我是小黑人";
}
}
为什么Callable接口使用FutureTask来启动线程?
1、启动线程方式通过Thread的构造方法 new Thread().start();
2、也可以通过new Thread(new Runable()).start()方法启动
3、看Runable接口的实现类有一个FutureTask类
4、FutureTask的构造方法需要传入Callable接口实现类.
5、这样就形成了new Thread(new FuntureTask(new Callable()).start();启动线程模式
6、Callable和FutureTask中的泛型V标识返回值类型
7、通过futureTask.get(); 方法获取返回值,注意: 这里可能会导致线程阻塞问题,因为需要等待返回结果。
8、多个线程一起执行,结果会有缓存,提高效率
常用辅助类
- CountDownLatch
package com.xiaohei.countDownLatchDemo;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"奥利给");
countDownLatch.countDown();
}
// 县城没有执行完,一直会处于等待状态,直到countDownLatch为0,自动释放
countDownLatch.await();
System.out.println("奥利给嗷嗷嗷嗷");
}
}
- CyclicBarrier(加法计数器)
package com.xiaohei.countDownLatchDemo;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CilicBarrier {
public static void main(String[] args) {
// 加法计数器,当所有线程都到达某个屏障点后再进行后续的操作
CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
System.out.println("奥利给1111");
});
// 如果当达到屏障点时,会触发执行任务,然后清空计数器,这里有一个清空操作,当每次在达到阈值时,再次触发逻辑
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"奥利给");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
CountDownLatch和CyclicBarrier的区别
CountDownLatch | CyclicBarrier |
---|---|
减计数方式 | 加计数方式 |
计数为0时释放所有等待线程 | 计数达到指定屏障值时释放所有线程 |
计数为0时无法重置 | 计数达到指定屏障值时可以重置 |
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没有影响 | 调用await()方法计数加1,加1后的值不等于构造方法给的值则线程阻塞 |
不可重复利用 | 可以重复利用 |
- semaphore(信号量)
package com.xiaohei.countDownLatchDemo;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
// 信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
int finalI = i;
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+ "进来");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+ "出去");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}).start();
}
}
}
阻塞队列 BlockingQueue
Collection下的一个接口
使用阻塞队列
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | pu(t) | offer() |
移除 | remove() | poll() | take() | poll() |
检查队首元素 | elment() | peek() | - | - |
测试
- 抛出异常,add()和remove()
// 测试1: 抛出异常
public static void throwExceptionBlockingDueue(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//
System.out.println(arrayBlockingQueue.add("1"));
System.out.println(arrayBlockingQueue.add("2"));
System.out.println(arrayBlockingQueue.add("3"));
// 如果添加元素大于指定阻塞队列大小就会抛出异常:
// Exception in thread "main" java.lang.IllegalStateException: Queue full
// System.out.println(arrayBlockingQueue.add("4"));
System.out.println("===========");
// 如果移除元素数量超出阻塞队列长度抛出异常:
// Exception in thread "main" java.util.NoSuchElementException
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
}
- 有返回值,不抛出异常,如果添加元素数量超出阻塞队列长度不会抛出异常,而是返回false,如果阻塞队列为空获取元素会返回null, offer()和poll()
public static void noThrowExpectionBlockingDueue(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("1"));
System.out.println(arrayBlockingQueue.offer("2"));
System.out.println(arrayBlockingQueue.offer("3"));
// 如果添加元素数量超出阻塞队列长度不会抛出异常,而是返回false
System.out.println(arrayBlockingQueue.offer("4"));
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
// 如果添加元素数量超出阻塞队列长度不会抛出异常,而是返回null
System.out.println(arrayBlockingQueue.poll());
}
- 阻塞等待(一直等待) put()和take()方法
// 测试3: 阻塞等待
public static void blockingDueueWait() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.put("1");
arrayBlockingQueue.put("2");
arrayBlockingQueue.put("3");
// 如果添加元素超过阻塞队列长度,会一直等待
// arrayBlockingQueue.put("4");
arrayBlockingQueue.take();
arrayBlockingQueue.take();
arrayBlockingQueue.take();
// 如果阻塞队列为空,获取元素会一直等待
arrayBlockingQueue.take();
}
- 超时等待(超出一定时间自动结束) offer和poll的重载方法
public static void chaoBlockingDueueWait() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("1",1, TimeUnit.SECONDS);
blockingQueue.offer("2",1, TimeUnit.SECONDS);
blockingQueue.offer("3",1, TimeUnit.SECONDS);
// 添加元素超出阻塞队列长度,过1秒会自动结束
blockingQueue.offer("4",1, TimeUnit.SECONDS);
blockingQueue.poll(1,TimeUnit.SECONDS);
blockingQueue.poll(1,TimeUnit.SECONDS);
blockingQueue.poll(1,TimeUnit.SECONDS);
// 如果阻塞队列元素为空.再移除元素会等待,过1秒会自动结束
blockingQueue.poll(1,TimeUnit.SECONDS);
}
- 获取队首元素
抛异常方法使用element()
有返回值,不抛出异常使用take()
内容总结
以上是互联网集市为您收集整理的JUC并发编程全部内容,希望文章能够帮你解决JUC并发编程所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。