JUC并发编程

回顾多线程

线程和进程

1.线程是在进程下进行的,
2.线程是进程的最小单位
3.一个进程是由多个线程组成的
4.Java默认的有两个线程:main、GC
5.Java真的可以开启线程吗?
不可以,只能通过本地方法去调用,native本地方法,底层的C++,Java无法直接操作硬件。

并发和并行

  1. 解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
  2. 解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
  3. 解释三:并行是在多台处理器上同时处理多个任务。如 hadoop 分布式集群,并发是在一台处理器上“同时”处理多个任务。
  4. 解释四:并发:多线程操作同一个资源;并行:多个人一起走路

获取CPU的核数:

  1. 任务管理器
    在这里插入图片描述
  2. 通过代码来实现
System.out.println(Runtime.getRuntime().availableProcessors());

线程的几个状态:

//源码中的说明
 public enum State {
      //新生
        NEW,
	  //运行
        RUNNABLE,
	  //阻塞
        BLOCKED,
	  //等待
        WAITING,
	  //超时等待,也属于阻塞的一种
        TIMED_WAITING,
	  //终止
        TERMINATED;
    }

wait和sleep区别:

  1. 来自不同的类
    wait–>Object;sleep–>Thread

  2. 关于锁的释放
    wait会释放锁,sleep不会释放锁

  3. 使用的范围是不同的
    wait:必须在同步代码块中;sleep:可以在任何地方

  4. 是否需要捕获异常
    wait:不需要捕获异常;sleep:必须要捕获异常

synchronized和Lock的区别

//synchroized
 public static void main(String[] args) {
        //多个线程操作同一资源类,把资源丢入线程
        Ticket ticket = new Ticket();
        //lambda表达式,jdk1.8新特性,把资源丢入线程
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//OOP
class Ticket{
    //属性加方法
    private int num =50;
    public synchronized void sale(){
        if (num>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"张票,剩余:"+num);
        }
    }
//Lock
public static void main(String[] args) {
        //多个线程操作同一资源类,把资源丢入线程
        Ticket2 ticket2 = new Ticket2();
        //lambda表达式,jdk1.8新特性,把资源丢入线程
        new Thread(()-> { for (int i = 1; i < 60; i++) ticket2.sale();},"A").start();
        new Thread(()-> {for (int i = 1; i < 60; i++)  ticket2.sale(); },"B").start();
        new Thread(()-> {for (int i = 1; i < 60; i++)  ticket2.sale(); },"C").start();
    }
}
//OOP
class Ticket2{
    //属性加方法
    private int num =50;
    //Lock三部曲:new ReentrantLock();加锁,解锁
    Lock lock=new ReentrantLock();
    public synchronized void sale(){
        lock.lock();
        try{
            if (num>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"张票,剩余:"+num);
            }
        }finally {
            lock.unlock();
        }

    }

区别:

  1. synchronized内置的Java关键字,Lock是一个Java类
  2. synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
  3. synchronized会自动释放锁,Lock必须手动释放锁。如果不手动释放锁,会产生死锁
  4. synchronized线程1(获得锁,阻塞)、线程2(一直等待);Lock锁就不一定会一直等待下去
  5. synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码。
  6. synchronized可重入锁,是不可以中断的,非公平的:lock,可重入锁,非公平(公平与否可以自己通过代码实现)源码介绍如下图:
    在这里插入图片描述

生产者和消费者

传统的生产者和消费者问题

public static void main(String[] args) {
        Date date = new Date();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

    }
}
class Date{
    private int num=0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if (num!=0){
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        this.notifyAll();//通知其他线程,+1完毕
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if (num==0){
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        this.notifyAll();//通知其他线程,—1完毕
    }

运行结果为:
在这里插入图片描述
如果此时有ABCD四个线程,同时进行+1和-1,此时就会出错,会出现其他的各种数字。参考jdk文档,我们需要把if改成while就可改正这个错误,文档说明如下图。不过此时的运行结果四条线程是随机出现的,并没有什么规律,后期可以用Lock锁的Conditoin进行唤醒指定的线程,
在这里插入图片描述

Lock版的生产者和消费者问题

在这里插入图片描述
Synchronized和Lock的对比:
在这里插入图片描述

class Date2{
    private int num=0;
    Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();
    //+1
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num!=0){
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            //通知其他线程,+1完毕
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //-1
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num==0){
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            //通知其他线程,—1完毕
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();

主要代码也就是下面这几行:

    Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();
    lock.lock();//加锁
    condition.await();//等待
    condition.signalAll();//唤醒
    lock.unlock();//解锁

Lock方法为什么比synchnorized的方法更好用呢?

因为Lock中的Condition可以实现精准唤醒线程,如果同时有几个线程,notify方法操作下,线程运行比较随意,而condition.signalAll中可以精准的操作去唤醒哪个线程。

有三条线程,ABCA依次进行执行,A -> B -> C -> A

 private Lock lock=new ReentrantLock();
    private Condition condition1=lock.newCondition();
    private Condition condition2=lock.newCondition();
    private Condition condition3=lock.newCondition();
    private int num=1;

    public void printA(){
        lock.lock();
        try {
            while (num!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"AAAAAAAAAAA");
            //唤醒指定的B
            num=2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (num!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"BBBBBBBB");
            num=3; //唤醒指定的C
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            while (num!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"CCCCCCCCCCC");
            num=1; //唤醒指定的A
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

运行结果就是和我们设想的是相同的,按照ABCA依次进行执行的。

八锁现象进行对锁的理解

//Test01
  public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone.Call();
        },"A").start();
    }
}
class Phone{
    public synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public synchronized void Call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}
//Test02
 public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(()->{
            try {
                phone.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone.hello();
        },"A").start();
    }
}
class Phone2{
    public synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

    public void hello(){
        System.out.println("hello");
    }
}
//Test03
 public static void main(String[] args) {
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        new Thread(()->{
            try {
                phone1.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone2.Call();
        },"A").start();
        new Thread(()->{
            phone2.hello();
        },"A").start();
    }
}
class Phone3{
    public synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public synchronized void Call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}

//Test04
 public static void main(String[] args) {
        Phone4 phone = new Phone4();
        new Thread(()->{
            try {
                phone.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone.Call();
        },"A").start();

    }
}
class Phone4{
    public static synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public static synchronized void Call(){
        System.out.println("打电话");
    }
//Test05
 public static void main(String[] args) {
        Phone5 phone1 = new Phone5();
        Phone5 phone2 = new Phone5();
        new Thread(()->{
            try {
                phone1.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone2.Call();
        },"A").start();
    }
}
class Phone5{
    public static synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public static synchronized void Call(){
        System.out.println("打电话");
    }
//Test06
 public static void main(String[] args) {
        Phone6 phone1 = new Phone6();

        new Thread(()->{
            try {
                phone1.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone1.Call();
        },"A").start();
    }
}
class Phone6{
    public static synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public  synchronized void Call(){
        System.out.println("打电话");
    }
//Test07
 public static void main(String[] args) {
        Phone7 phone1 = new Phone7();
        Phone7 phone2 = new Phone7();

        new Thread(()->{
            try {
                phone1.Send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            phone2.Call();
        },"A").start();
    }
}
class Phone7{
    public static synchronized void Send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }
    public  synchronized void Call(){
        System.out.println("打电话");
    }

以上几种情况对比分析:

  1. Test01:
    两个对象同时用一把锁,谁先拿到这个锁,谁就先执行
    运行结果是:1.发短信,打电话

  2. Test02:
    一个线程加锁,一个线程不加锁,执行的时候首先执行的是不加锁的那个线程,然后执行的是加锁的那个线程
    运行结果:1.hello 2.发短信

  3. Test03:
    两个线程两个锁,此时就看哪个线程有没有延迟,发短信那个锁加了一个4s的延时,所以此时打电话这个线程先执行
    运行结果:1.打电话 2.发短信

  4. Test04:
    两个线程一个锁,此时加了static静态代码块,synchronized锁的对象是方法的调用者,同时加入了static,此时锁的是class,一个类只有一个class对象,
    类一加载就有了,static走的是class对象,锁的也是class。
    运行结果:1.发短信 2.打电话

  5. Test05:
    两个线程两个锁,此时加了static静态方法,运行结果和Test03不同,此时static锁的是class,两个对象的class对象是一个模板,一个类只有一个class
    运行结果:1.发短信 2.打电话

  6. Test06:
    一个是静态代码块(static),一个是普通代码块,静态代码块锁的是class模板(发短息),普通的代码锁的是调用者,打电话的方法不需要去等待发短信的锁
    运行结果:1.打电话 2.发信息

  7. Test07:
    静态方法走的是class模板(发短信),后面走的是普通方法,不需要等待
    运行结果:1.打电话 2.发信息

集合类不安全

在并发的情况下,我们使用的List,Map、Set都是不安全的。

List在单线程的时候是安全的,但是在多线程的时候就是不安全的。

//单线程
public static void main(String[] args) {
        List<String>list= Arrays.asList("a","b","c");
        list.forEach(System.out::println);
    }

在这里插入图片描述

//多线程
public static void main(String[] args) {
      List<String > list  = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();

        }
    }

在这里插入图片描述

CopyOnWriteArrayList

  • 此时会报错java.util.ConcurrentModificationException(并发修改异常),此时有三种解决方案:
  • 方案一:List<String > list = new Vector<>();
  • 方案二:List<String>list= Collections.synchronizedList(new ArrayList<>());
  • 方案三:List<String>list= new CopyOnWriteArrayList<>();

CopyOnWriteArrayList写入时复制,是计算机设计领域的一种优化,简称COW,用来提高效率。多个线程调用的时候,在读取的时候是固定的,在写入的时候覆盖。为了避免覆盖的发生,造成数据问题就用CopyOnWrite。

CopyOnWrite比Vector好在哪里?

通过如下两张源码的对比图可以发现,Vector中使用的是synchronized方法,一般效率比较低。而CopyOnWrite使用的是新版的Lock的方法,效率较高
在这里插入图片描述

在这里插入图片描述

CopyOnWriteArraySet

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

在这里插入图片描述
同理可得解决方案如下:

  1. Set set =Collections.synchronizedSet(new HashSet<>());
  2. Set set =new ConcurrentSkipListSet<>();

HashSet底层是什么?

public HashSet() {
        map = new HashMap<>();
    }
//add本质是map key 是无法重复的
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

HashMap默认因子的相关源码:
在这里插入图片描述

ConcurrentHashMap

 public static void main(String[] args) {
        //0.75默认的加载因子
        //默认的加载因子: new HashMap<>(16,0.75);
        HashMap<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }

    }

在这里插入图片描述
修改如下:

//并发下的HashMap
        Map<String, String> map = new ConcurrentHashMap<>();

查看API文档,如下图:
在这里插入图片描述

Callable

Callable也是线程的实现方法之一,一般用Runnable较多。Callable需要返回结果
在这里插入图片描述

由API文档可知:

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同run() / call()

注意:

callable 不能直接用Thread开启。可以通过Runnable中的FutureTask方法进行实现。相关API文档如下图:
在这里插入图片描述
在这里插入图片描述

 public static void main(String[] args) throws ExecutionException, InterruptedException {
 		//实现过程如下:
        //new Thread(new Runnable()).start;
        //new Thread(new new FutureTask<>()).start;
        //new Thread(new new FutureTask<>(Callable)).start;
        MyThread thread = new MyThread();
        FutureTask task = new FutureTask(thread);
        new Thread(task,"A").start();
        //get方法获取返回Callable的返回结果,
        //可能会产生阻塞,解决方法:把他放到最后,或者用异步通信来处理
        String  o =(String ) task.get();
        System.out.println(o);
    }
}
class MyThread implements Callable<String >{
    @Override
    public String  call() throws Exception {
        System.out.println("call()");
        return "1234567789";
    }
}

在这里插入图片描述
如果此时有两个线程,同时开启,结果只打印一次。

常用的辅助类

CountDownLatch

在这里插入图片描述

//减法计数器
 public static void main(String[] args) throws InterruptedException {
        //总数为6,必须是执行任务的时候,使用
        CountDownLatch downLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go Out");
                downLatch.countDown();//数量-1
            },String.valueOf(i)).start();

            downLatch.await();//等待计数器归零,然后在往下执行
            System.out.println("Close Door");
        }
    }

常用的方法如下:

downLatch.countDown();//数量-1
downLatch.await();//等待计数器归零,然后在往下执行

CyclicBarrier

在这里插入图片描述

//加法计数器
public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙");
        });
        for (int i = 0; i < 7; i++) {
        //lambda表达式不能直接读取外部的i,需要借助于final来进行实现
            final int temp=i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+temp+"龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

在这里插入图片描述

Semaphore

在这里插入图片描述

 public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; 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();//释放
                }
            },String.valueOf(i)).start();
        }

在这里插入图片描述
重点方法:

semaphore.acquire();//得到,如果已经满了,等待,等待被释放为止
semaphore.release();//释放,会将当前的信号量+1,然后唤醒等待的线程

作用:多个共享资源互斥的使用,并发限流,控制最大的线程数

读写锁

在这里插入图片描述

  1. 独占锁(写锁)一次只能被一个线程占有
  2. 共享锁(读锁)多个线程可以同时占有
  3. ReadWriteLock
    读-读 可以共存
    读-写 不能共存
    写-写 不能共存
  public static void main(String[] args) {
        MyCache myCache = new MyCache();
        //写入
        for (int i = 1; i <= 5; i++) {
            final int temp=i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        //读取
        for (int i = 1; i <= 5; i++) {
            final int temp=i;
            new Thread(()->{
                myCache.pop(temp+"");
            },String.valueOf(i)).start();
        }

    }

}
//volatile只能保证可见性,不能保证原子性
class MyCache{
    private volatile Map<String,Object>map=new HashMap();
    //读写锁
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    private Lock lock=new ReentrantLock();


    //存,写入的东西,一次只能进去一个线程
    public void put(String key,String value){
        //写的时候是按照顺序依次进行,而读的时候就是随机的
        readWriteLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }

    }
    //取,读,所有人都可以读
    public void pop(String key){
        readWriteLock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }

    }
}

在这里插入图片描述

阻塞队列

阻塞队列的图文解释:
在这里插入图片描述

BlockingQueue

BlockingQueue我们并不陌生,通过查看API文档我们可以看出他和List和Set之间的关系
在这里插入图片描述
在这里插入图片描述
队列就是先进先出,双端队列就是两个端口可以同时进行取出

在这里插入图片描述

什么时候我们会使用阻塞队列?
多线程并发处理,线程池

方式抛出异常有返回值,不抛出异常阻塞,等待超时等待
添加add()offer()put()offer()
移除remove()poll()take()poll()
检测队首元素elementpeek--
 public static void test01(){
        //没有返回值,会抛出异常
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));
        //抛出异常,java.lang.IllegalStateException: Queue full
        //System.out.println(arrayBlockingQueue.add("d"));

        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
    }
	 public static void test02(){
        //有反回值,不抛出异常
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
       // System.out.println(blockingQueue.offer("d"));//不会抛出异常,返回一个false
        System.out.println(blockingQueue.element());//查看队首元素是谁

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//返回值为null,没有抛出异常

    }
public static void test03() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("d");//队列没有位置了,一直等待

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //System.out.println(blockingQueue.take());//没有这个元素,一直阻塞
    }

 public static void test04() throws InterruptedException {
        //有反回值,不抛出异常
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        //blockingQueue.offer("c",2, TimeUnit.SECONDS);//等待2s,如果没有位置就退出



        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//等待2s,如果没有就退出

    }

SynchronousQueue同步队列

没有容量,必须进去一个元素,等这个元素被取出后,才能在放进去一个元素。put (), take()
BlockingQueue是SynchronousQueue的父类,SynchronousQueue与其他的方法不同,必须等待放进去的类取出来后,才能接着放入下一个

 public static void main(String[] args) {
        BlockingQueue<String> blockingQueue=new SynchronousQueue<>();//同步队列
        new Thread(()->{

            try {
                System.out.println(Thread.currentThread().getName()+"put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"2").start();

    }

运行结果如下:
在这里插入图片描述

线程池(重点)

池化技术以及线程创建方法

 public static void main(String[] args) {
        //方法一:单个线程
        ExecutorService threadPoll = Executors.newSingleThreadExecutor();//单一的
        //方法二:固定线程大小
        ExecutorService threadPoll = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
        //方法三:可伸缩的线程
        ExecutorService threadPoll = Executors.newCachedThreadPool();//遇强则强,可变得

       try{
           for (int i = 0; i < 10; i++) {
               //使用了线程池之后,需要用线程池来创建现成
               threadPoll.execute(()->{
                   System.out.println(Thread.currentThread().getName()+" ok");
               });
           }
       } catch (Exception e) {
           e.printStackTrace();
       }finally {
           //使用完现成之后,必须要关闭线程
           threadPoll.shutdown();
       }
    }

单一线程
固定线程
可伸缩线程

源码分析:

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
     public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    //本质:都用的是ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                              int maximumPoolSize,//最大核心线程池大小
                              long keepAliveTime,//超时了没有人调用就会被释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂:创建线程的,一般不用动
                              RejectedExecutionHandler handler//拒绝策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
    

7大参数

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                              int maximumPoolSize,//最大核心线程池大小
                              long keepAliveTime,//超时了没有人调用就会被释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂:创建线程的,一般不用动
                              RejectedExecutionHandler handler//拒绝策略)

在这里插入图片描述

四种拒绝策略

四种拒绝策略
第一种:

  ExecutorService threadPoll = new ThreadPoolExecutor(
                2,//一开始的时候的两个业务员
                5,//最大业务员办理容量
                3,//办理业务员不忙的时候的不开放的个数
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),//休息区的个数
                Executors.defaultThreadFactory(),//线程工厂,一般是不动的
                new ThreadPoolExecutor.AbortPolicy());//银行满了,此时还有人过来,银行不处理这个业务员,抛出异常

在这里插入图片描述
第二种:

  new ThreadPoolExecutor.CallerRunsPolicy());//哪来的去哪里

在这里插入图片描述
第三种:

 new ThreadPoolExecutor.DiscardPolicy());//队列满了,不会抛出异常,会直接丢掉任务

运行结果
在这里插入图片描述
第四种:
线程较少时,没有什么反应,一般使用与线程较多的时候

 new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试去和最早的竞争,也不会抛出异常

IO密集型,CPU密集型:(调优)

 public static void main(String[] args) {
        //获取CPU核数
        System.out.println(Runtime.getRuntime().availableProcessors());
        
        ExecutorService threadPoll = new ThreadPoolExecutor(
                2,//一开始的时候的两个业务员
                5,//最大业务员办理容量
                3,//办理业务员不忙的时候的不开放的个数
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),//休息区的个数
                Executors.defaultThreadFactory(),//线程工厂,一般是不动的
                new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试去和最早的竞争,也不会抛出异常


        try{
            for (int i = 1; i <=9; i++) {
                //使用了线程池之后,需要用线程池来创建现成
                threadPoll.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //使用完现成之后,必须要关闭线程
            threadPoll.shutdown();
        }
    }
}

池的最大发的大小如何定义设置:
1.CPU密集型,几核,就是几,可以保持CPU效率最高
2.IO密集型>判断你的程序中十分耗IO的线程
程序:15个大型任务,IO十分占用资源

四大函数式接口(必须掌握)

函数式接口

函数式接口:只有一个方法的接口
例如:

@FunctionalInterface
public interface Runnable {
 
    public abstract void run();
}
//超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量使用
//foreach(消费者类的函数接口)

在这里插入图片描述

Function函数式接口

源码:

在这里插入图片描述

 //Function函数式接口:有一个输入参数,有一个输出
    //只要是函数式接口就可以用lambda表达式
    public static void main(String[] args) {
        //匿名内部类
        Function function=new Function<String,String >() {
            @Override
            public String apply(String str) {
                return str;
            }
        };

        //Function function1= (str)->{ return str;};
        System.out.println(function.apply("asp"));
    }
}

运行结果:
在这里插入图片描述

Predicate段定型接口

源码:

段定型接口

代码演示:

  //段定型接口:有一个输入参数,,返回值只能是布尔型
 public static void main(String[] args) {
        //判断字符串是否为空
       Predicate<String> predicate= new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return str.isEmpty();
            }
        };
        System.out.println(predicate.test("asd"));
    }
}

运行结果:
在这里插入图片描述

Consumer消费型接口

在这里插入图片描述

 //Consumer:消费型接口:只有输入,没有返回值
    public static void main(String[] args) {
        //Consumer<String> consumer = new Consumer<String>() {
        //   @Override
        // public void accept(String str) {
        //    System.out.println(str);
        //}
        // };
        // consumer.accept("asd");
        Consumer<String> consumer = (str) -> {
            System.out.println(str);
        };
        consumer.accept("asd");

    }

Supplier供给型接口

在这里插入图片描述

//Supplier:供给型接口,没有参数,只有返回值
    public static void main(String[] args) {
        Supplier supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
               System.out.println("get()");
                return 1024;
           }
        };
        //Supplier supplier =()->{return 1024;};
        System.out.println(supplier.get());
    }
}

Stream流式计算

 /*
    现在有五个用户,进行筛选
    1.ID是偶数
    2.年龄大于23
    3.用户名转换为大写字母
    4.用户名字母倒着写
    5.只输出一个用户
    * */
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(5,"e",25);

        //集合就是存储
        List<User> list= Arrays.asList(u1,u2,u3,u4,u5);
        //计算交给Stream流运算
        list.stream()
                .filter(u->{return u.getId()%2==0;})//filter->筛选
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getStr().toUpperCase();})//转换为大写字母
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})//排序
                .limit(1)
                .forEach(System.out::println);//打印
    }

ForkJoin

什么是ForkJoin?
ForkJoi在jdk1.7并行执行任务!提高效率,大数据量
大数据:MapReduce本质就是:把大任务拆分为小任务

在这里插入图片描述

ForkJoin工作窃取:
如下图所示:A线程和B线程,此时B线程已经执行完毕,A线程才执行了一半,B线程则会把A线程没有执行的部分偷过来给执行,提高效率,维护的都是双端队列
在这里插入图片描述

ForkJoin用法:
在这里插入图片描述

在这里插入图片描述

如何使用ForkJoin:
1.通过ForkJoinPool来执行
2.计算任务forkJoinPool.execute(ForkJoinTask task)
3.计算类要继承ForkJoinTask

public class ForkJoinDemo extends RecursiveTask<Long> {
    private long start;
    private long end;
    //临界值,中间值
    private long temp=10000L;

    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }

    //计算方法
    @Override
    protected Long compute() {
        long sum = 0L;
        if ((end - start) < temp) {
            for (long i = start; i < end; i++) {
                sum = +i;
            }
            return sum;
        } else {
            long middle=(end-start)/2;//中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork();//拆分任务,把任务压入队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
            task2.fork();//拆分任务,把任务压入队列
            return task1.join()+task2.join();
        }
    }
}

测试:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1();//时间为:365
        //test2();//数字运行不出来
        test3();//时间为:215
    }
    public static void test1(){
        long sum = 0L;
        long start=System.currentTimeMillis();
            for (long i = 1L; i <= 10_0000_0000; i++) {
                sum +=i;
            }

        long end=System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间为:"+(end-start));
    }
    public static void test2() throws ExecutionException, InterruptedException {
        long start=System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务
        Long sum=submit.get();//会存在阻塞等待
        long end=System.currentTimeMillis();
        System.out.println("sum="+sum+"时间为:"+(end-start));
    }
    public static void test3(){
        long start=System.currentTimeMillis();
        //Stream并行流,规约计算
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end=System.currentTimeMillis();
        System.out.println("sum="+sum+"时间为:"+(end-start));
    }
}

异步回调

Future设计的初衷:对将来的某个事件的结果进行建模

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值