JUC笔记整理

JUC

尚硅谷JUC视频

1. 概述

线程和进程的区别

进程是程序运行的基本单位,一个程序运行就是一个进程。

线程是cpu调度的基本单位,共享内存

1.1 JUC

juc是java.util.courrent 工具包的简称,jdk1.5之后出现的

1.2 线程状态

新建,就绪,运行,阻塞,死亡
在这里插入图片描述

1.3 wait 和sleep的区别

sleep 需要指定睡眠时间,wait不需要

sleep是Thread类的方法,wait是object方法,必须在同步代码块中使用(synchronized)

sleep不会释放对象的锁,wait会释放对象的锁

1.4 守护线程和非守护线程

守护线程就是一种后台进程,当所以的用户线程结束后才会停止

非守护线程:普通的线程

2. Lock 接口

2.1 Synchronized

修饰同步代码块或者同步方法

synchronized定义方法,不能被继承

方法: 普通方法锁的是this, 静态方法锁定是Class类

代码块:锁定的是监视器对象 synchronized(this)

class Ticket {
	//票数
	private int number = 30;
	//操作方法:卖票
	public synchronized void sale() {
	//判断:是否有票
		if(number > 0) { 
            System.out.println(Thread.currentThread().getName()+" :"+(number--)+" "+number);
     }
    }
}
2.2 Lock

Lock和Synchronized的区别:

​ Synchronized不需要手动释放锁,Lock需要手动释放锁

Synchronized是JVM层面,Lock属于API层面

​ Synchronized是非公平锁,Lock可以是公平锁和非公平锁

​ Synchronized不能被中断,Lock可以被中断和精准唤醒

2.3 Lock接口
public interface Lock 
{ void lock();
  void lockInterruptibly() throws InterruptedException;
  boolean tryLock();
  boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  void unlock();
  Condition newCondition();
}
1. lock

获取锁如果锁被其他线程获取,进入等待

需要手动释放锁

Lock lock = new ReetrantLock();
lock.lock();
try {
    
}catch(Exeception e) {
    
}finally {
    lock.unlock();
}
2.newCondition

Synchronized的wait()和notify()来实现等待和通知

Lock通过newCondition()返回Condition对象来实现等待通知

await() == wait()

sigal() == notify()

2.4 ReentrantLock

可重入锁

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Airmal{
    private int number;
    public Lock lock=new ReentrantLock();
    public  Condition condition=lock.newCondition();
    public void incrnumber(){
        lock.lock();

        try {
           while (number!=0){
               condition.await();
           }
           number++;
           System.out.println(Thread.currentThread().getName()+"\t"+number);
           condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }
    public void decrenumber(){
        lock.lock();

        try {
              while (number==0){
                  condition.await();
              }
              number--;
              System.out.println(Thread.currentThread().getName()+"\t"+number);
              condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }

}
public class ProdeCustomerLockDemo {
    public static void main(String[]ags){
        Airmal airmal=new Airmal();
        new Thread(()->{
           for (int i=1;i<=10;i++){
               airmal.incrnumber();
           }
        },"A").start();

        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.decrenumber();
            }
        },"B").start();

        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.incrnumber();
            }
        },"C").start();
        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.decrenumber();
            }
        },"D").start();
    }
}
 
 

精准唤醒

A B C 三个线程启动,要A-B-C线程依次执行,A打印5次.B打印10,C打印15次

思路:一把锁配多分钥匙,通过标志位来判断

//Condition 精确控制

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * A B C 三个线程
 * A -B-C 依次执行
 * A 打印5 次
 * B 打印 10
 * C 打印 15 次
 */
class Airmals{
    private int number=1;  //标志位  1:A B:2 C:3
    private Lock lock=new ReentrantLock();
    Condition c1=lock.newCondition();
   Condition c2=lock.newCondition();
   Condition c3=lock.newCondition();
    public void pring5(){
        lock.lock();
        try {
              while (number!=1){
                  c1.await();
              }
           for (int i=1;i<=5;i++){
               System.out.println(Thread.currentThread().getName()+"\t"+i);
           }

           number=2;
        c2.signal(); //唤醒B
        }catch (Exception e){

        }finally {
             lock.unlock();
        }

    }

    public void pring10(){
        lock.lock();
        try {
             while (number!=2){
                 c2.await();
             }
             for (int i=1;i<=10;i++){
                 System.out.println(Thread.currentThread().getName()+"\t"+i);
             }
             number=3;
             c3.signal();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }

   public  void pring15(){
          lock.lock();
          try {
              while (number!=3){
                  c3.await();
              }
              for (int i=1;i<=15;i++){
                  System.out.println(Thread.currentThread().getName()+"\t"+i);
              }
              number=1;
              c1.signal();
          }catch (Exception e){

          }finally {
              lock.unlock();
          }
   }

}
public class ConditionDemo {
    public static void main(String[] args) {
        Airmals airmals=new Airmals();
        new Thread(()->{
                 for (int i=1;i<=10;i++){
                     airmals.pring5();
                 }
        },"A").start();

        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmals.pring10();
            }
        },"B").start();

        new Thread(()->{
            for (int i=1;i<=15;i++){
                airmals.pring15();
            }
        },"C").start();
    }
}
 
 

2.5 ReadWirteLock

这是一个接口,实现类ReentrantReadWriteLock

ReentrantReadWriteLock 里面提供了很多丰富的方法,不过最主要的有两个 方法:readLock()和 writeLock()用来获取读锁和写锁。

为解决Lock不管读写都锁的效率低,ReadWriteLock读不上锁,写上锁

//多个线程操作一个资源
//读-读可以存在
//读-写不能并存
//写-写不能并存
class  MyLock{
    private Map<String,Object> mylock=new HashMap<>();
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    public  void put(String key,Object value) throws InterruptedException {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"---写入数据");
            TimeUnit.MICROSECONDS.sleep(300);
            System.out.println(Thread.currentThread().getName()+"\t"+"---写入数据成功");
        }catch (Exception e){
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }
    public void get(String key) throws InterruptedException {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"----读取数据");
            TimeUnit.MICROSECONDS.sleep(300);
            System.out.println(Thread.currentThread().getName()+"\t"+"---读取数据成功");
        }catch (Exception e){

        }finally {
            readWriteLock.readLock().unlock();
        }

    }
}
public class ReadWriteLockDemo {
    public static void main(String[] args) {
       MyLock myLock=new MyLock();
       for (int i=1;i<=3;i++){
           final  String temp= valueOf(i);
           //写入操作
           new Thread(()->{
               try {
                   myLock.put(temp, new Object());
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }, valueOf(i)).start();

       }
        for (int i=1;i<=3;i++){
            final  String temp= valueOf(i);
            new Thread(()->{
                try {
                    myLock.get(temp);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, valueOf(i)).start();
        }
    }
}

1 —写入数据
1 —写入数据成功
2 —写入数据
2 —写入数据成功
3 —写入数据
3 —写入数据成功
1 ----读取数据
2 ----读取数据
3 ----读取数据
1 —读取数据成功
2 —读取数据成功
3 —读取数据成功


3.线程之间通信

通信模型:共享内存和消息传递

3.1 synchronized方案

高内聚低耦合

/**
 * 生成者消费者demo
 * 线程编程:
 *    1.高内聚低耦合,线程操作资源
 *    2.判断/干活/通知
 *    3.防止虚假唤醒
 */

class Airmant{
    private  int number=0;

    public synchronized void  increnumber() throws InterruptedException {
        //判断
        while (number!=0){
            this.wait();
        }
        //干活
        number++;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        //通知
        this.notifyAll();
    }
    public  synchronized  void decrennumber() throws InterruptedException {
        //判断
        while (number==0){
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+"\t"+number);

        //通知
        this.notifyAll();

    }
}
public class ProdeCustomerDemo {
     public static void  main(String[]ags){
         Airmant airmant=new Airmant(); //高内聚

         new Thread(()->{

                      try {
                       for(int i=1;i<=10;i++){
                           airmant.increnumber();
                       }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }

         },"A").start();

         new Thread(()->{
             try {
                 for(int i=1;i<=10;i++){
                     airmant.decrennumber();
                 }

             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         },"B").start();


     }
}
 
 

3.2 Lock方案
class Airmal{
    private int number;
    public Lock lock=new ReentrantLock();
    public  Condition condition=lock.newCondition();
    public void incrnumber(){
        lock.lock();
        try {
           while (number!=0){
               condition.await();
           }
           number++;
           System.out.println(Thread.currentThread().getName()+"\t"+number);
           condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }
    public void decrenumber(){
        lock.lock();

        try {
              while (number==0){
                  condition.await();
              }
              number--;
              System.out.println(Thread.currentThread().getName()+"\t"+number);
              condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }

}
public class ProdeCustomerLockDemo {
    public static void main(String[]ags){
        Airmal airmal=new Airmal();
        new Thread(()->{
           for (int i=1;i<=10;i++){
               airmal.incrnumber();
           }
        },"A").start();

        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.decrenumber();
            }
        },"B").start();

        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.incrnumber();
            }
        },"C").start();
        new Thread(()->{
            for (int i=1;i<=10;i++){
                airmal.decrenumber();
            }
        },"D").start();
    }
}

4.集合线程

java.util.ConcurrentModificationExceptio

public class NotSafeDemo {
/**
* 多个线程同时对集合进行修改
* @param args
*/
public static void main(String[] args) {  
    List list = new ArrayList();
    for (int i = 0; i < 100; i++) {
        new Thread(() ->{
           list.add(UUID.randomUUID().toString());
            list.forEach(System.out::println);
        }, "线程" + i).start();
     }
   }
}
4.1 List

底层:new ArrayList本质上是new 一个Object类型的数组

默认大小为10,在jdk1.7会初始化为10,jdk1.8采用懒加载在add的时候才会初始化

默认扩容为原来的1.5倍

底层的复制采用的是Arrays.copyof() - > System.copyof()

解决方案

Vector

public class nosaleCollection {
    public static void main(String[]ags){
       /* List <String>list=new ArrayList();*/
        List <String>list=new Vector<>();
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

Collections.synchronized

public class nosaleCollection {
    public static void main(String[]ags){
        List<String>list= Collections.synchronizedList(new ArrayList<>());
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

CopyOnWriteArrayList 写时复制

采用读写分离的思想,保证最终一致性,写入数据的时候创建一个新的容器,将原来的数据复制,添加元素到新的容器中,原来的引用指向新的容器

优点: 读写分离,并发度高

缺点: 需要创建新的容器,可能频繁GC

​ 写的时候读取数据不一致,不能保证强一致性,保证最终一致性

public class nosaleCollection {
    public static void main(String[]ags){
        List<String> list=new CopyOnWriteArrayList<>();
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}
 
4.2 Map

默认大小是16,扩容为原来的1倍,如16变成32

解决

Collections.synchronizedMap

ConcurrentHashMap

HashTable

4.3 Set

HashSet底层采用的是HashMap

HashSet的add()方法加只有一个参数,hashMap(key,value)原因是haspMap(e,object)

解决方案

Collections.synchronizedSet

public class nosaleCollection {
    public static void main(String[]ags){
        Set <String>set=Collections.synchronizedSet(new HashSet<>());
        for (int i=1;i<=30;i++){
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
            },String.valueOf(i)).start();
        }

    }

    private static void nosaftList() {
        List<String> list=new CopyOnWriteArrayList<>();
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

CopyOnWriteHashSet

public class nosaleCollection {
    public static void main(String[]ags){
        Set <String>set=new CopyOnWriteArraySet<>();
        for (int i=1;i<=30;i++){
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }

    }

    private static void nosaftList() {
        List<String> list=new CopyOnWriteArrayList<>();
        for(int i=1;i<=30;i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

5.多线程问题

class Phone {
    public static synchronized void sendSMS() throws Exception {
  	//停留 4 秒TimeUnit.SECONDS.sleep(4);
    System.out.println("------sendSMS");
	}
	public synchronized void sendEmail() throws Exception {
        System.out.println(" ---- sendEmail");
    }
	public void getHello() { 
        System.out.println(" --- getHello");
	}
}

1 标准访问,先打印短信还是邮件 ------sendSMS ------sendEmail

2 停 4 秒在短信方法内,先打印短信还是邮件 ------sendSMS ------sendEmail

3 新增普通的hello 方法,是先打短信还是 hello ------getHello ------sendSMS

4 现在有两部手机,先打印短信还是邮件 ------sendEmail ------sendSMS

5 两个静态同步方法,1 部手机,先打印短信还是邮件 ------sendSMS ------sendEmail

6 两个静态同步方法,2 部手机,先打印短信还是邮件 ------sendSMS ------sendEmail

7 1 个静态同步方法,1 个普通同步方法,1 部手机,先打印短信还是邮件 ------sendEmail ------sendSMS

8 1 个静态同步方法,1 个普通同步方法,2 部手机,先打印短信还是邮件 ------sendEmail ------sendSMS

具体表现为以下 3 种形式。

​ 对于普通同步方法,锁是当前实例对象。

​ 对于静态同步方法,锁是当前类的Class 对象。

​ 对于同步方法块,锁是Synchonized 括号里配置的对象

6. Callable

6.1 实现线程的方式
  • 继承Thread

  • 实现Runnable接口

  • 实现Callable接口

  • 线程池创建

6.2 Callable接口
//有返回值的线程接口 callable
class MyThread implements Callable<Integer>
{

    @Override
    public Integer call() throws Exception {
        System.out.println("======Callable==========");
        return 1024;
    }
}
public class CallbackDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new MyThread());
        new Thread(futureTask,"a").start();
        Integer task = (Integer) futureTask.get();
        System.out.println(task);
    }
}
 
6.3 Future接口
public boolean cancel(boolean mayInterrupt):用于停止任务
如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true 时才会中断任务
 public Object get()抛出 InterruptedExceptionExecutionException:用于获取任务的结果。 如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果    
public boolean isDone():如果任务完成,则返回 true,否则返回 false
6.4 FutureTask

在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些 作业交给 Future 对象在后台完成

  • 主线程需要的时候通过Future对计算结果
  • 多用于耗时的计算

7.JUC三大辅助类

7.1 CountDownLatch

减少计数

CountDownLatch 类设置一个计数器

通过countDown方法减少1,使用await等待计数器不大于0,让后执行await之后的语句

countdownlatch 调用await方法,这个线程会阻塞

countDown方法计数器减1

当计数器为0,await阻塞的线程会被唤醒

//6个人全部离开,主main才关门
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
      CountDownLatch countDownLatch=new CountDownLatch(6);
        for (int i=1;i<=6;i++)
        {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t  离开教室");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println("main 关门");
    }
}
7.2 CyclicBarrier

循环栅栏

CyclicBarrier 看英文单词可以看出大概就是循环阻塞的意思,在使用中 CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一 次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后 的语句。可以将 CyclicBarrier 理解为加 1 操作

public class CyclicBrrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
            System.out.println("*****召唤神龙");
        });
        for (int i=1;i<=7;i++){
            final  Integer temp=i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t 收集了"+temp+"龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}
7.3 Semaphore

Semaphore 的构造方法中传入的第一个参数是最大信号量(可以看成最大线 程池),每个信号量初始化为一个最多只能分发一个许可证。使用 acquire 方 法获得许可证,release 方法释放许可

场景: 抢车位, 6 部汽车 3 个停车位

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore=new Semaphore(3); //模拟资源类,有3个空车位
        for (int i=1;i<=6;i++){
            new Thread(()->{
                try {
                    semaphore.acquire(); //数字-1
                    System.out.println(Thread.currentThread().getName()+"\t抢占了车位");
                    TimeUnit.SECONDS.sleep(4);
                    System.out.println(Thread.currentThread().getName()+"\t离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release(); //数字+1
                }
            },String.valueOf(i)).start();
        }
    }
}

8.读写锁

对共享资源进行读写的时候,写操作不是那么频繁。

读情况下不需要加锁

ReentrantReadWriteLock 它表示两个锁,一个是读操作为共享锁;一个写相关的锁,为排它锁

读锁只能读锁

写锁其他不能加读锁和写锁

//多个线程操作一个资源
//读-读可以存在
//读-写不能并存
//写-写不能并存
class  MyLock{
    private Map<String,Object> mylock=new HashMap<>();
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    public  void put(String key,Object value) throws InterruptedException {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"---写入数据");
            TimeUnit.MICROSECONDS.sleep(300);
            System.out.println(Thread.currentThread().getName()+"\t"+"---写入数据成功");
        }catch (Exception e){
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }
    public void get(String key) throws InterruptedException {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"----读取数据");
            TimeUnit.MICROSECONDS.sleep(300);
            System.out.println(Thread.currentThread().getName()+"\t"+"---读取数据成功");
        }catch (Exception e){

        }finally {
            readWriteLock.readLock().unlock();
        }

    }
}
public class ReadWriteLockDemo {
    public static void main(String[] args) {
       MyLock myLock=new MyLock();
       for (int i=1;i<=3;i++){
           final  String temp= valueOf(i);
           //写入操作
           new Thread(()->{
               try {
                   myLock.put(temp, new Object());
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }, valueOf(i)).start();

       }
        for (int i=1;i<=3;i++){
            final  String temp= valueOf(i);
            new Thread(()->{
                try {
                    myLock.get(temp);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, valueOf(i)).start();
        }
    }
}

9.阻塞队列

9.1 BlockingQueue

队列FIFO

阻塞队列具有阻塞功能,没有元素的时候获取元素会阻塞线程释放CPU

队列满的时候增加元素的时候会阻塞

会自动唤醒线程

方法类型抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()take()poll(time,unit)
检查element()peek()不可用不可用
抛出异常当阻塞队列满时,在add会抛出异常 当阻塞队列为空,remove会抛出异常
特殊值插入方法,成功true 否则为false 移除方法,成功队列元素,没有就为null
一直阻塞队满加入,阻塞或者响应中断 队列为空 ,take会一直阻塞消费者线程
超时退出队满,队列阻塞生成者一段时间。
/**
* 阻塞队列
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// List list = new ArrayList();
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//第一组
// System.out.println(blockingQueue.add("a"));
// System.out.println(blockingQueue.add("b"));
// System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.element());
//System.out.println(blockingQueue.add("x"));
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// 第二组
// System.out.println(blockingQueue.offer("a"));
// System.out.println(blockingQueue.offer("b"));
// System.out.println(blockingQueue.offer("c"));
// System.out.println(blockingQueue.offer("x"));
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// 第三组
// blockingQueue.put("a");
// blockingQueue.put("b");
// blockingQueue.put("c");
// //blockingQueue.put("x");
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// 第四组
   System.out.println(blockingQueue.offer("a"));
   System.out.println(blockingQueue.offer("b"));
   System.out.println(blockingQueue.offer("c"));
   System.out.println(blockingQueue.offer("a",3L, TimeUnit.SECONDS));
 }
}

9.2 种类
1.ArrayBlockingQueue

数组组成的有界阻塞队列

维护了一个定长数组

保存两个整型变量,分别标识队列的头部和尾部在数组中的位置

生产者放入数据和消费者消费数据,公用一个同一个对象锁,LinkedBlockingQueue使用的不是同一个对象锁

与LinkedBlockingQueue不同的还有插入删除会产生一个额外的Node对象,ArrayBlockingQueue不会产生

2. LinkedBlockingQueue

链表组成的有阻塞队列

大小为Integer.MAX_VALUE

该队列由一个链表构成

LinkedBlockingQueue 之所以能够高效的处理并发数据,还因为其对于生 产者端和消费者端分别采用了独立的锁来控制数据同步

3.PriorityBlockingQueue

优先级排序的无界阻塞队列

优先级的判断是通过Compator对象决定

不会阻塞生产者,只会没有消费者可消费的数据时,才会阻塞消费者

生产者生产数据的速度绝对不能快于消费者消费 数据的速度

4.SynchronousQueue

不存储元素的阻塞队列,也即单个元素的队列

5. DelayQueue

使用优先级队列实现的延迟无界阻塞队列

DelayQueue 中的元素只有当其指定的延迟时间到了,才能够从队列中获取到 该元素。DelayQueue 是一个没有大小限制的队列,因此往队列中插入数据的 操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻 塞。

6. LinkedTransferQueue

由链表组成的无界阻塞队列

预占模式

消费者获取元素的时候,如果队列不为空,取元素

队列为空,生成一个节点(元素为null),生成者入队时发现为null节点,生产者不入队,将元素填充。并唤醒该节点等待的线程,被唤醒的线程取元素

7. LinkedBlockingDeque

由链表组成的双向阻塞队列

9.3 小结
  1. 在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件 满足,被挂起的线程又会自动被唤起

  2. 为什么需要 BlockingQueue? 在 concurrent 包发布以前,在多线程环境下, 我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全, 而这会给我们的程序带来不小的复杂度。使用后我们不需要关心什么时候需要 阻塞线程,什么时候需要唤醒线程,因为这一切 BlockingQueue 都给你一手 包办了

10.线程池

存放线程的池子

不需要每次去创建和销毁线程降低资源销毁

提高响应速度

便于线程的管理

在这里插入图片描述

10.1 核心参数
  • corePoolSize 核心线程数
  • maxinumPoolSize 最大线程数
  • keepAliveTime 空闲线程存活时间
  • unit 时间单位
  • workQueue 阻塞队列
  • threadFactory 工厂
  • handler 拒绝策略
10.2 原理

提交任务,当前线程数 < corepoolsize 创建核心线程处理

currentThreadNum > corePoolSize 但阻塞队列没有满时放入阻塞队列中

阻塞队列满了的时候,线程数<= maxinumpoolsize时 ,创建非核心线程数进行处理

线程数 > maxinumpoolsize 时,采用拒绝策略

10.3 拒绝策略

AbortPolicy: 丢弃任务,线程池默认的拒绝策略

CallerRunsPolicy:回退给调用者

DiscardPolicy :直接丢弃

DiscardOldestPolicy: 触发拒绝策略,丢弃最老的一个

10.4 Executors工具类
1.newCachedThreadPool

作用:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空 闲线程,若无可回收,则新建线程

  • 线程数没有固定,可以达到Integer.MAX_VALUE
  • 线程池可以对缓存重复利用和回收
  • 当线程池中没有可用线程会重新创建一个线程
/**
* 可缓存线程池
* @return
*/
public static ExecutorService newCachedThreadPool(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(0,
	Integer.MAX_VALUE,
	60L,
	TimeUnit.SECONDS,
	new SynchronousQueue<>(),
	Executors.defaultThreadFactory(),
	new ThreadPoolExecutor.AbortPolicy());
}

场景: 适用于创建一个可无限扩大的线程池,服务器负载压力较轻,执行时间较 短,任务多的场景

2.newFixedThreadPool

创建一个可重用固定线程数的线程池

线程池中的线程处于一定的量,可以很好的控制线程的并发量

• 线程可以重复被使用,在显示关闭之前,都将一直存在

• 超出一定量的线程被提交时候需在队列中等待

/**
* 固定长度线程池
* @return
*/
public static ExecutorService newFixedThreadPool(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(10,
	10,
	0L,
	TimeUnit.SECONDS,
	new LinkedBlockingQueue<>(),
	Executors.defaultThreadFactory(),
	new ThreadPoolExecutor.AbortPolicy());
}

场景: 适用于可以预测线程数量的业务中,或者服务器负载较重,对线程数有严 格限制的场景

3.newSingleThreadExecutor

创建一个使用单个 worker 线程的 Executor

特征: 线程池中最多执行 1 个线程,之后提交的线程活动将会排在队列中以此 执行

/**
* 单一线程池
* @return
*/
public static ExecutorService newSingleThreadExecutor(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(1,
	1,
	0L,
	TimeUnit.SECONDS,
	new LinkedBlockingQueue<>(),
	Executors.defaultThreadFactory(),
	new ThreadPoolExecutor.AbortPolicy());
}

场景: 适用于需要保证顺序执行各个任务,并且在任意时间点,不会同时有多个 线程的场景

public class ThreadPoolDemo1 {
/**
* 火车站 3 个售票口, 10 个用户买票
* @param args
*/
public static void main(String[] args) {
//定时线程次:线程数量为 3---窗口数为 3
ExecutorService threadService = new ThreadPoolExecutor(3,
	3,
	60L,
	TimeUnit.SECONDS,
	new LinkedBlockingQueue<>(),
	Executors.defaultThreadFactory(),
                                                       
    new ThreadPoolExecutor.DiscardOldestPolicy());
	try {
		//10 个人买票
		for (int i = 1; i <= 10; i++) {
            	threadService.execute(()->{
					try {
						System.out.println(Thread.currentThread().getName() + "窗口,开始卖票");
						Thread.sleep(5000);
						System.out.println(Thread.currentThread().getName() + "窗口买票结束");
					}catch (Exception e){
                        e.printStackTrace();
   					});
         
 	}catch (Exception e){
             e.printStackTrace();
    }finally {
     //完成后结束
     }
10.5 线程创建方式

不通过Executors创建线程,因为 FixedThreadPool 和SingleThreadExecutor底层采用的LinkedBlockingQueue最长度为Integer.MAX_VALUE (OOM), CacheThreadPool的最大线程数为Integer.MAX_VALUE(CPU压力)

10.6 手动创建

new ThreadPoolExecutor来创建对象


11.Fork /Join

fork 将大任务拆分成小任务

join 将任务进行合并

1.Fork

1.ForkJoinTask

首先需要创建一个 ForkJoin 任务。 该类提供了在任务中执行 fork 和 join 的机制。通常情况下我们不需要直接集 成 ForkJoinTask 类,只需要继承它的子类,Fork/Join 框架提供了两个子类:

a.RecursiveAction:用于没有返回结果的任务

b.RecursiveTask:用于有返回结果的任务

2.ForkJoinPool

ForkJoinPool :ForkJoinTask 需要通过 ForkJoinPool 来执行

3.RecursiveTask:

继承后可以实现递归(自己调自己)调用的任务

2.实例
/**
* 递归累加
*/
public class TaskExample extends RecursiveTask<Long> {
    private int start;
    private int end;
	private long sum;
	/**
	* 构造函数
	* @param start
	* @param end
	*/
	public TaskExample(int start, int end){
        this.start = start;
        this.end = end;
    }
	/**
	* The main computation performed by this task.
	*
	* @return the result of the computation
	*/
    @Override
	protected Long compute() {
		System.out.println("任务" + start + "=========" + end + "累加开始");
		//大于 100 个数相加切分,小于直接加
		if(end - start <= 100){
			for (int i = start; i <= end; i++) {
				//累加
				sum += i;
			}
		}else {
			//切分为 2 块
			int middle = start + 100;
			//递归调用,切分为 2 个小任务
			TaskExample taskExample1 = new TaskExample(start, middle);
			TaskExample taskExample2 = new TaskExample(middle + 1, end);
			//执行:异步
			taskExample1.fork();
			taskExample2.fork();
			//同步阻塞获取执行结果
			sum = taskExample1.join() + taskExample2.join();
		}
			//加完返回
			return sum;
	}
}
public class ForkJoinPoolDemo {
/**
* 生成一个计算任务,计算 1+2+3 ........+1000
* @param args
*/
public static void main(String[] args) {
	//定义任务
	TaskExample taskExample = new TaskExample(1, 1000);
	//定义执行对象
	ForkJoinPool forkJoinPool = new ForkJoinPool();
	//加入任务执行
	ForkJoinTask<Long> result = forkJoinPool.submit(taskExample);
	//输出结果
	try {
		System.out.println(result.get());
	}catch (Exception e){
        e.printStackTrace();
	}finally {
		forkJoinPool.shutdown();
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值