JDK 中Concurrent包的基础知识
阻塞队列BlockingQueue
BlockingQueue
通过建立线程安全的队列,从而快速方便的设计高质量的多线程程序。BlockingQueue
通常应用在消费者生产者场景。举个例子:一个电动汽车,只要有电,他就能运动。
BlockingQueue接口的具体实现
BlockingQueue生产者消费者实例
- Producer
public class Producer implements Runnable {
private static Logger logger= LoggerFactory.getLogger(Producer.class);
// 线程是否在运行
public static boolean isRunning=true;
// 阻塞队列
private BlockingQueue blockingQueue=null;
// 随机数的最大范围值
private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;
// 随机数对象
private Random random=new Random();
public Producer(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
logger.info("生产者线程启动!......");
logger.info("生产者线程id为==========="+Thread.currentThread().getId()+"===========");
long id = Thread.currentThread().getId();
try{
while(isRunning){
int num = random.nextInt(DEFAULT_RANGE_FOR_SLEEP);
System.out.println("生产者放入阻塞队列中的数据为======"+num);
blockingQueue.put(num);
Thread.sleep(3000);
}
}catch (Exception e){
e.printStackTrace();
logger.error(e.getMessage());
}finally {
logger.info("生产者线程"+id+"正在中止!");
Thread.currentThread().interrupt();
logger.info("生产者线程结束!......");
}
}
}
- Consumer
public class Consumer implements Runnable {
private Logger logger= LoggerFactory.getLogger(Consumer.class);
protected BlockingQueue queue=null;
private static boolean isRunning=true;
public Consumer(BlockingQueue queue) {
this.queue = queue;
}
@Override
public void run() {
try{
logger.info("消费者线程启动......");
logger.info("消费者线程id为======="+Thread.currentThread().getId()+"==========");
while(isRunning){
Object take = queue.take();
String s = String.valueOf(take);
if(s.endsWith("4") || s.endsWith("3")){
System.out.println("找到想要的东西了生产者结束生产!..........");
Producer.isRunning=false;
isRunning=false;
}
if(s==null || s.equals(" ")){
logger.info("--------生产者队列空了------------");
}else {
System.out.println("消费者消费的数据为:" + queue.take());
}
}
}catch (Exception e){
logger.error(e.getMessage());
}finally {
logger.info("消费者线程"+Thread.currentThread().getId()+"正在中止!");
logger.info("消费者消费结束!......");
}
}
}
- BlockingQequeTest
public class BlockingQequeTest {
private static Logger logger= LoggerFactory.getLogger(BlockingQequeTest.class);
public static void main(String[] args) {
// 申明一个容量为10的缓存队列
BlockingQueue blockingQueue=new ArrayBlockingQueue(10);
logger.info("阻塞队列测试开始!......");
Producer producer=new Producer(blockingQueue);
Consumer consumer=new Consumer(blockingQueue);
new Thread(producer).start();
new Thread(consumer).start();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
}
}
}
BlockingQeque接口中的方法
// 给定元素放到指定的队列中
BlockingQueue.add(java.lang.Object)
BlockingQueue.remove(java.lang.Object)
BlockingQueue.put(java.lang.Object) throws java.lang.InterruptedException
BlockingQueue.contains(java.lang.Object)
BlockingQueue.poll(long,java.util.
BlockingQueue.offer(java.lang.Object,long,java.util.
BlockingQueue.offer(java.lang.Object)
BlockingQueue.drainTo(java.util.Collection)
BlockingQueue.drainTo(java.util.Collection,int)
// 获取队列中剩余的空间
BlockingQueue.remainingCapacity()
// 从队列中获取值,如果队列中没有值,线程会一直阻塞
BlockingQueue.take() throws java.lang.InterruptedException
DelayQueue 延时队列
DelayQueue
是一个BlockingQueue
无界阻塞队列,要实现DelayQueue
延时队列要实现Delayed
接口,队列元素需要实现getDelay(TimeUnit unit)
方法和compareTo(Delayed o)
方法, getDelay
定义了剩余到期时间,compareTo
方法定义了元素排序规则。
DelayQueue延时队列实例
- Order类
public class Order implements Delayed {
@JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8")
private long time;
String name;
public Order(long time, String name,TimeUnit unit) {
this.time = System.currentTimeMillis() + (time > 0? unit.toMillis(time): 0);
this.name = name;
}
@Override
public long getDelay(TimeUnit unit) {
return time-System.currentTimeMillis();
}
@Override
public int compareTo(Delayed o) {
Order order= (Order) o;
long l = this.time - order.time;
if(l<=0){
return -1;
}else {
return 1;
}
}
@Override
public String toString() {
return "Order{" +
"time=" + time +
", name='" + name + '\'' +
'}';
}
}
- DelayQueueTest
public class DelayQueueTest {
public static void main(String[] args) {
Order order=new Order(5,"order1", TimeUnit.SECONDS);
Order order1=new Order(10,"order2", TimeUnit.SECONDS);
Order order2=new Order(15,"order3", TimeUnit.SECONDS);
DelayQueue<Order> delayQueue=new DelayQueue<>();
delayQueue.put(order1);
delayQueue.put(order2);
delayQueue.put(order);
System.out.println("开始时间:"+ TimeUtils.getCurrentDateTime());
for (int i = 0; i < 3; i++) {
Order order3 = null;
try {
order3 = delayQueue.take();
System.out.format("name:{%s}, time:{%s}\n",order3.name, TimeUtils.getCurrentDateTime());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ExecutorService 执行器服务
interface ExecutorService
表示一个异步执行机制,是我们能够在后台执行任务,ExecutorService
的实现就是一个线程池的实现。
Demo
@Test
public void test1(){
// 使用NewFixedThreadPool创建一个ExecutorService,将一个Runnable接口的是实现立类传给execute()方法,
// ExecutorService中的某个线程会执行该Runnable
ExecutorService executorService= Executors.newFixedThreadPool(10);
executorService.execute(()->{
System.out.println("当前线程Id为"+Thread.currentThread().getId());
}
);
executorService.shutdown();
}
CountDownLatch
CountDownLatch
是一个计数的锁,通过countDown()
方法将此计数值减为0同时唤起之前await()
调用的线程,一般用于某些任务执行完成后再执行其他任务的场景中。
Demo
public class CountDownLatchTest {
private static Logger logger= LoggerFactory.getLogger(CountDownLatch.class);
/**
* 场景描述:有一个主线程正在跑,当主线程完成后其他的线程才能开始工作,当其他线程都结束后主线程才能结束
*/
@Test
public void test() throws InterruptedException {
CountDownLatch startFlag=new CountDownLatch(1);
CountDownLatch other=new CountDownLatch(5);
// 创建并且启动5个线程
for (int i = 0; i < 5; i++) {
new Thread(()->{
try {
Thread.sleep(4000);
// 等待主线程发出开始的信号
startFlag.await();
System.out.println("其他线程"+Thread.currentThread().getId()+"正在执行...........");
// 其他线程执行完成,释放一个完成信号
other.countDown();
logger.info("其他线程执行结束...........");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
logger.info("主线程正在执行!..........");
logger.info("主线程执行结束,其他线程开始执行!..........");
// 主线程执行完成,发出开始信号,其他线程开始执行
startFlag.countDown();
System.out.println("主线程已经执行完成正在等待其他线程执行结束");
other.await();
}
}
线程池执行者ThreadPoolExecutor
ThreadPoolExecutor
是ExecutorService
接口的一个实现,ThreadPoolExecutor
包含的线程池能够包含不同数量的线程,池中线程的数量由核心线程大小和最大线程数量决定。
ThreadPoolExecutor构造函数
@Test
public void test1(){
int corePoolSize=5;
int maxPoolSize=10;
long keepAliveTime=500;
LinkedBlockingQueue linkedBlockingQueue=new LinkedBlockingQueue();
for (int i = 0; i < 3; i++) {
linkedBlockingQueue.add(new Thread(()->{
System.out.println("当前线程的Id为"+Thread.currentThread().getId());
}));
}
ExecutorService executorService= (ExecutorService) new ThreadPoolExecutor(corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
linkedBlockingQueue);
List<Runnable> runnables = executorService.shutdownNow();
runnables.stream().forEach(x->{
System.out.println(x);
});
executorService.shutdown();
}
Lock锁
如果一段代码被Synchronized
修饰了。当一个线程获取了对应的锁,并执行该代码块时,其他线程只能一直等待,等待获取锁的线程释放锁,才去执行别的代码。Synchronized
代码块不能保证进入访问等待线程的先后顺序。Synchroized
块必须完整地包含在单个方法里,Lock对象可以把它的Lock()
和unLock()
方法的调用放在不同的方法里面。
接口包含的方法
lock()
lockInterruptibly() throws java.lang.InterruptedException
newCondition()
tryLock()
tryLock(long,java.util.concurrent.TimeUnit) throws java.lang.InterruptedException
unlock()
Lock中锁的使用方法
Lock()
方法是平常使用最多的一个方法,用来获取锁,如果锁已经被占用则进行等待。采用Lock,必须主动去释放锁,在发生异常的时候不会释放锁,在Finallly后进行释放锁的操作。
Lock lock=....;
lock.lock();
try{
// 处理任务
}catch(Exception e){
}finally{
// finally中释放锁
lock.unlock();
}
tryLock()
方法是有返回值的,它表示用来尝试获取锁,如果获取成功则返回true;如果获取失败(锁已经被其他线程占用)则返回false。当拿不到锁的时候会一直等待。
Lock lock=....;
if(lock.tryLock()){
try{
// 处理任务
}catch(Exception e){
}finally(){
// 释放锁
lock.unlock();
}
}else{
// 如果拿不到锁,则进行其他操作
}
- 使用Lock锁
@Test
public void test1(){
Lock lock=new ReentrantLock();
for (int i = 0; i < 5; i++) {
int finalI = i;
lock.lock();
try {
new Thread(() -> {
Thread.currentThread().setName("线程" + finalI);
System.out.println("当前的线程为"+Thread.currentThread().getId()+"线程名称为:"+Thread.currentThread().getName());
}).start();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
try {
Thread.sleep(6000);
System.out.println("主线程信息为:"+Thread.currentThread().getId()+"============================");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
CountDownLatch使用
使用场景:主线程在等子线程全部执行完成、释放资源后再执行主线程的任务
- 例子
@RestController
@RequestMapping("/thread")
public class ThreadPoolTestController {
public static final Logger logger = LoggerFactory.getLogger(ThreadPoolTestController.class);
/**
* 线程池的基本大小
*/
final static int CORE_POOL_SIZE = 10;
/**
* 线程池允许的最大线程数
*/
static int MAX_POOL_SIZE = 20;
@RequestMapping("/test1")
public void test1() {
logger.info("=====>CountDawnlanch测试开始...");
ExecutorService executorService = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100));
int num = 10000;
CountDownLatch countDownLatch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
int finalI = i;
try {
executorService.execute(() -> {
try {
for (int j = 0; j < finalI; j++) {
logger.info("=====>线程" + finalI + "正在处理第" + j + "个任务");
}
logger.info("=====>子线程的ID========================={}", Thread.currentThread().getId());
} catch (Exception e) {
logger.info("=====>发生错误", e.getMessage(), e);
} finally {
countDownLatch.countDown();
logger.info("=====>当前countDownLatch的数为:" + countDownLatch.getCount());
}
});
}catch (Exception e){
logger.error("=====>线程中出现错误",e);
countDownLatch.countDown();
}
}
try {
countDownLatch.await();
logger.info("=====>子线程中的任务执行完成");
logger.info("=====================>主线程的信息为================{}", Thread.currentThread().getId());
} catch (InterruptedException interruptedException) {
logger.info("=====>countDownLatch出错", interruptedException);
}
}
}
。。。。卧槽看的吐了,搞不下去了有时间再看吧。