学姐天天缠着问我JUC,搞得我没有时间打游戏,无奈之下我写下JUC基础,要她自己去研究 上

引言

今天是星期一,翘了课在寝室玩农药,正当我要五杀的时候,学姐给我打了个电话,于是我的五杀没了。
在这里插入图片描述
“学弟,今天面试官问我了不了解JUC,没答出来怎么办?”学姐哭着问道
“面试官怎么说的啊?”我把自己痛失五杀的愤怒强行压制下去,毕竟这个是在我大一的时候对我照顾有加的学姐,无论是正常的校园生活和不正常的校园生活,要我舒舒服服的混到了大三,是、人要有一颗感恩的心,所以我还是忍住没有和她发脾气。
“面试官说日后再说”,学姐说道
“这样的吗?这个面试官可真不是什么好东西?”
“为什么这么说啊”?学姐不解的说道。
“没事学姐,我给你写一篇JUC的文章,你有时间看看就好了”
在这里插入图片描述

什么是 JUC

JUC就是 java.util 下的工具包、包、分类等。

在这里插入图片描述
“在我们学习JUC之前,先去回顾一下线程方面的知识,学姐你要是不会的话可以子去看一下别的博主的博客,我以后有时间会写”,我说道
“好的学弟”

普通的线程

  • Thread
  • Runnable 没有返回值、效率相比入 Callable 相对较低!
  • Callable 有返回值!

线程和进程

我之前就有写过一篇关于线程和进程的文章,可以先看看这篇文章

  • 进程:一个程序,QQ.exe Music.exe 程序的集合;
  • 一个进程往往可以包含多个线程,至少包含一个
  • Java默认有2个线程? mian、GC
  • 线程:开了一个进程 Typora,写字,自动保存(线程负责的)
  • 对于Java而言提供了:Thread、Runnable、Callable操作线程。

并发、并行

并发

当我们有若干个线程需要运行的时候,由于cpu处理速度很快,他会把若干个线程快速交替,就是多线程同时操作资源

并行

假如我们的电脑是36核的,36个cpu同时运行

可以用这个代码来检查一下自己电脑是几核的,我的是16核

public class Test1 { 
    public static void main(String[] args) { 
	  // 获取cpu的核数 
     // CPU 密集型,IO密集型 
  	  System.out.println(Runtime.getRuntime().availableProcessors());
     // 如果电脑是8核,则结果输出8
 	} 
}

注:
并发编程的本质就是充分利用CPU的资源

线程有几个状态

答案:6个

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.
     * 线程运行中
     */
    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}.
     * 线程阻塞状态
     */
    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 <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * 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;
}

wait/sleep 区别

第一:两者来自不同的父类

  • wait => Object
  • sleep => Thread

第二:两者释放锁不一样

  • wait 会释放锁
  • sleep 睡觉了,抱着锁睡觉,不会释放!

第三:两者使用的范围是不同的

  • wait 必须在同步代码块中使用
  • sleep 可以再任何地方睡眠

Synchronized锁

传统 Synchronized锁:一个简单的卖票例子

package com.znb;

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

public class Test01 {
   public static void main(String[] args) {
        //并发:多个线程同时操作一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();

        // @FunctionalInterface 函数式接口,jdk1.8 lambada表达式
        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                ticket.sale();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                ticket.sale();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}

//资源类 OOP
class Ticket {
    //属性、方法
    private int number = 50;

    // 卖票的方式
    // synchronized 本质: 队列,锁
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" +
                    (50-(--number)) + "张票,剩余:" + number + "张票");
        }
    }

}

Lock锁

公平锁(FairLock)

十分公平,分先来后到,不准插队

非公平锁 (UnFairLock)

不公平,可以插队

一般都是默认非公平锁,因为有时候如果一个线程的延时是10分钟,另一个线程没有延时,如果延时十分钟的线程先到,那么后一个线程就要等十分钟。

将上面的卖票例子用lock锁 替换synchronized:

package com.znb;

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

public class Test01 {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
        }},"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
        }},"C").start();
    }
}

class Ticket{
    private  int number =100;
    Lock lock=new ReentrantLock();
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"张票子");
        }
    }
}

Synchronized 和 Lock 区别:

  • Synchronized 内置的Java关键字, Lock 是一个Java类
  • Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以判断锁,非公平(可以自己设置)
  • Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
  • Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,就会死锁
  • Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
  • Synchronized 线程 1(获得锁,如果线程1阻塞)、线程2(等待);Lock锁就不一定会等待下去;

生产者和消费者问题

非JUC版

可以参考一下我之前的博客

package com.znb.pc;

public class Test01 {
    public static void main(String[] args) {
        A a=new A();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class A{
    private int sum=0;
    public synchronized void incr() throws InterruptedException {
        while(sum!=0){
            this.wait();
        }
        sum++;
        System.out.println(Thread.currentThread().getName()+"==》"+sum);
        this.notifyAll();
    }
    public synchronized void decr() throws InterruptedException {
        while (sum==0){
            this.wait();
        }
        sum--;
        System.out.println(Thread.currentThread().getName()+"==》"+sum);
        this.notifyAll();
    }
}

这里我们通过线程A唤醒B,B唤醒C以此类推,并且这里我们用的是while,因为如果我们用的是if,并且线程大于2个,那么就会存在虚假唤醒,出现错误
在这里插入图片描述

使用while的结果

在这里插入图片描述

使用if的结果

在这里插入图片描述
我们可以看见,出现了-169,-170这样错误的数据,所以这里记得用while

JUC版

这里引进一个新东西,叫Condition

在这里插入图片描述

代码实现

package com.znb.pc;

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

public class Test02 {
    public static void main(String[] args) {
        B a=new B();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    a.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class B{
    private int sum=0;
    Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();
    public  void incr() throws InterruptedException {
        lock.lock();
        try {
            while(sum!=0){
                condition.await();
            }
            sum++;
            System.out.println(Thread.currentThread().getName()+"==》"+sum);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public  void decr() throws InterruptedException {
        lock.lock();
        try {
            while (sum==0){
                condition.await();
            }
            sum--;
            System.out.println(Thread.currentThread().getName()+"==》"+sum);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

当我们使用Condition 的时候,我们唤醒线程和要线程等待就不是用什么 waitsleepnotifyAll这些东西了,我们需要使用 await 等待,signalAll唤醒

在这里插入图片描述

但是细心的我们发现,我们线程的顺序是A->B->C->D,但是最后输出的答案并不是这样,那要怎么办呢?没关系JDK源码的作者也想到了这个问题。

package com.znb.pc;

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

public class Test03 {
    public static void main(String[] args) {
        C c=new C();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.printf1();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.printf2();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.printf3();
            }
        },"C").start();
    }
}
class C{
    private Lock lock=new ReentrantLock();
    private Condition condition1=lock.newCondition();
    private Condition condition2=lock.newCondition();
    private Condition condition3=lock.newCondition();
    private int sum=1;
    public void printf1(){
        lock.lock();
        try {
            while (sum!=1){
                condition1.await();
            }
            System.out.println("你好");
            sum=2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printf2(){
        lock.lock();
        try {
            while (sum!=2){
                condition2.await();
            }
            System.out.println("我是帅哥");
            sum=3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printf3(){
        lock.lock();
        try {
            while (sum!=3){
                condition3.await();
            }
            System.out.println("你觉得呢");
            sum=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

在这里插入图片描述

8锁现象

8锁,就是关于锁的8个问题

package com.znb.eightlock;

import java.util.concurrent.TimeUnit;

public class Test01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        // 锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    // synchronized 锁的对象是方法的调用者!、
    // 两个方法用的是同一个对象调用(同一个锁),谁先拿到锁谁执行!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);// 抱着锁睡眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}


在这里插入图片描述

package com.znb.eightlock;



import java.util.concurrent.TimeUnit;

/**
 * 3、 增加了一个普通方法后!先执行发短信还是Hello?// 普通方法
 * 4、 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
 */
public class Test02  {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();

        new Thread(()->{
            phone2.hello();
        },"C").start();
    }
}

class Phone2{

    // synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    // 这里没有锁!不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }
}

// 先执行打电话,接着执行hello,最后执行发短信


在这里插入图片描述

package com.znb.eightlock;

import java.util.concurrent.TimeUnit;

public class Test03  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone3唯一的一个 Class 对象
class Phone3{

    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    // 类一加载就有了!锁的是Class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }
}



在这里插入图片描述

package com.znb.eightlock;

import java.util.concurrent.TimeUnit;

public class Test04  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone3唯一的一个 Class 对象
class Phone4{

    // 静态的同步方法 锁的是 Class 类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 普通的同步方法  锁的调用者(对象),二者锁的对象不同,所以不需要等待
    public synchronized void call(){
        System.out.println("打电话");
    }
}

// 7/8 两种情况下,都是先执行打电话,后执行发短信,因为二者锁的对象不同,
// 静态同步方法锁的是Class类模板,普通同步方法锁的是实例化的对象,
// 所以不用等待前者解锁后 后者才能执行,而是两者并行执行,因为发短信休眠4s
// 所以打电话先执行。


在这里插入图片描述

总结

每个对象以及每个class文件都会有自己的锁(实质是标志位),如果调用的方法是非静态方法,那么jvm会锁住调用的对象,但是如果是静态方法则会锁住class的锁,不同对象都会有自己的锁,他们之间互不干扰,而不同对象又共享 class 的锁。

集合类不安全

List

我们平时做一些学生项目的时候,例如个人博客,List应该用的很多,但是List的线程并不安全,我们来看一下代码

package com.znb.unsafe;

import java.util.*;
import java.util.function.Consumer;

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




    }
}

在这里插入图片描述
注意,如果这里没报错可能是你电脑太好, 把循环数变大看看比方说1000,10000,要是这样还没有报错那你的电脑是真的好

所以我们这里引入三种解决方法

  • 方案1、List list = new Vector<>();
  • 方案2、List list =Collections.synchronizedList(new ArrayList<>());
  • 方案3、List list = new CopyOnWriteArrayList<>();
package com.znb.unsafe;

import java.util.*;
import java.util.function.Consumer;

public class ListTest01 {
    public static void main(String[] args) {
        List<String> list= Collections.synchronizedList(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();
        }




    }
}

Set

Set、Hash 等在并发多线程条件下,不能实现数据共享,多个线程同时调用一个set对象时候就会出现并发修改异常ConcurrentModificationException

package com.znb.unsafe;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

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

在这里插入图片描述
注意,如果这里没报错可能是你电脑太好, 把循环数变大看看比方说1000,10000,要是这样还没有报错那你的电脑是真的好

所以我们这里引入两种种解决方法

  • 方案一:Set set =Collections.synchronizedSet(new HashSet<>());
  • 方案二:Set set=new CopyOnWriteArraySet();
package com.znb.unsafe;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

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

Map

我之前写了很长一篇关于hashmap的文章,可以先看一下
JDK1.8HashMap底层实现原理

package com.znb.unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class MapTest01 {
    public static void main(String[] args) {
        Map map=new ConcurrentHashMap();
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                map.put(UUID.randomUUID().toString().substring(0,5),"A");
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

Callable

在这里插入图片描述

Callable 和 Runable 对比:

  • Callable 是 java.util 包下 concurrent 下的接口,有返回值,可以抛出被检查的异常
  • Runable 是 java.lang 包下的接口,没有返回值,不可以抛出被检查的异常
  • 二者调用的方法不同,run()/ call()

同样的 Lock 和 Synchronized 二者的区别,前者是java.util 下的接口 后者是 java.lang 下的关键字。

package com.znb.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable()).start();// 启动Runnable
        // new Thread(new FutureTask<V>()).start();
        // new Thread(new FutureTask<V>( Callable )).start();
        new Thread().start(); // 怎么启动Callable?

        // new 一个MyThread实例
        MyThread thread = new MyThread();
        // MyThread实例放入FutureTask
        FutureTask futureTask = new FutureTask(thread); // 适配类

        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start(); // call()方法结果会被缓存,提高效率,因此只打印1个call

        // 这个get 方法可能会产生阻塞!把他放到最后
        Integer o = (Integer) futureTask.get();
        // 或者使用异步通信来处理!
        System.out.println(o);// 1024
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() {
        System.out.println("call()"); // A,B两个线程会打印几个call?(1个)
        // 耗时的操作
        return 1024;
    }
}

//class MyThread implements Runnable {
//
//    @Override
//    public void run() {
//        System.out.println("run()"); // 会打印几个run
//    }
//}

细节:

1、有缓存
2、结果可能需要等待,会阻塞!

CountDownLatch

减法计数器: 实现调用几次线程后 再触发某一个任务

在这里插入图片描述

package com.znb.jucutils;

import java.util.concurrent.CountDownLatch;

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() +" Go out");
                countDownLatch.countDown(); 
            },String.valueOf(i)).start();
        }

        countDownLatch.await(); 
        System.out.println("Close Door");

    }
}


在这里插入图片描述

CyclicBarrier

减法计数器: 实现调用几次线程后 再触发某一个任务
在这里插入图片描述

package com.znb.jucutils;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class  CyclicBarrierDemo {
    public static void main(String[] args) {


        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("后宫三千佳丽成功!");
        });

        for (int i = 1; i <=7 ; i++) {
            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

限流/抢车位!6车—3个停车位置
在这里插入图片描述

package com.znb.jucutils;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程数量:停车位! 限流!、
        // 如果已有3个线程执行(3个车位已满),则其他线程需要等待‘车位’释放后,才能执行!
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                // acquire() 得到
                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(); // release() 释放
                }

            },String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

读写锁 ReadWriteLock

ReadWriteLock 读可以被很多线程读,写只可以被一个线程写

package com.znb.rw;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class ReadWriteLockDemo {
    public static void main(String[] args) {
        //MyCache myCache = new MyCache();
        MyCacheLock myCacheLock = new MyCacheLock();

        // 写入
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }

        // 读取
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存
 * 加锁的
 */
class MyCacheLock{

    private volatile Map<String,Object> map = new HashMap<>();
 
    private ReadWriteLock readWriteLock = new
            ReentrantReadWriteLock();
   
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()
                    +"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()
                    +"写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

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

}


class MyCache{

    private volatile Map<String,Object> map = new HashMap<>();

    // 存,写
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()
                +"写入"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()
                +"写入OK");
    }

    // 取,读
    public void get(String key){
        System.out.println(Thread.currentThread().getName()
                +"读取"+key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()
                +"读取OK");
    }
}

在这里插入图片描述

  • 独占锁(写锁) 一次只能被一个线程占有
  • 共享锁(读锁) 多个线程可以同时占有
  • ReadWriteLock
  • 读-读 可以共存!
  • 读-写 不能共存!
  • 写-写 不能共存!

阻塞队列

在这里插入图片描述
BlockingQueue
BlockingQueue 不是新的东西

在这里插入图片描述
什么情况下我们会使用 阻塞队列?:多线程并发处理,线程池用的较多 !

学会使用队列

添加、移除

四组API

方式抛出异常有返回值,不抛出异常阻塞,等待超时等待
添加addofferputoffer
移除removepolltakepool
检查首元素elementpeek
package com.znb.Queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test01 {
    public static void main(String[] args) {
        test2();
    }
    public static void test1(){
        ArrayBlockingQueue 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.remove());

        System.out.println(blockingQueue.add("d"));
    }
    /**
     * 2. 有返回值,不抛出异常的方式
     */
    public static void test2(){
        // 队列的大小
        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.peek());

        // System.out.println(blockingQueue.offer("d"));
        // false 不抛出异常!

        System.out.println("===========================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());

        System.out.println(blockingQueue.poll());
        // null  不抛出异常!
    }
    /**
     * 3. 等待,阻塞(一直阻塞)
     */
    public static void test3() 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());
        // 没有这个元素,一直阻塞等待

    }


    /**
     * 4. 等待,阻塞(等待超时)
     */
    public static void test4() throws InterruptedException {
        // 队列的大小
        ArrayBlockingQueue blockingQueue =
                new ArrayBlockingQueue<>(3);

        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        // blockingQueue.offer("d",2,TimeUnit.SECONDS);
        // 等待超过2秒就退出

        System.out.println("===============");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        blockingQueue.poll(2, TimeUnit.SECONDS); // 等待超过2秒就退出

    }

}

在这里插入图片描述

SynchronousQueue

同步队列,没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素!

put、take

package com.znb.Queue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() +" put 1");
                // put进入一个元素
                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();
            }
        },"T1").start();


        new Thread(()->{
            try {
                // 睡眠3s取出一个元素
                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();
            }
        },"T2").start();
    }
}

在这里插入图片描述

线程池

池化技术

  • 程序的运行,本质:占用系统的资源! (优化资源的使用 => 池化技术)
  • 线程池、连接池、内存池、对象池///… 创建、销毁。十分浪费资源
  • 池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处:

  • 降低系统资源的消耗
  • 提高响应的速度
  • 方便管理

线程池:3大方法

在这里插入图片描述

package com.znb.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test01 {
    public static void main(String[] args) {
        // Executors 工具类、3大方法
        // Executors.newSingleThreadExecutor();// 创建单个线程的线程池
        // Executors.newFixedThreadPool(5);// 创建一个固定大小的线程池
        // Executors.newCachedThreadPool();// 创建一个可伸缩的线程池

        // 单个线程的线程池
        ExecutorService threadPool =
                Executors.newCachedThreadPool();

        try {
            for (int i = 1; i < 10; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(
                            Thread.currentThread().getName()+" ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}


7大参数

在这里插入图片描述
在这里插入图片描述

因为实际开发中工具类Executors 不安全,所以需要手动创建线程池,自定义7个参数。

package com.znb.pool;

import java.util.concurrent.*;

public class Test02 {
    public static void main(String[] args) {
        // 自定义线程池!工作 ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,// int corePoolSize, 核心线程池大小(候客区窗口2个)
                5,// int maximumPoolSize, 最大核心线程池大小(总共5个窗口)
                3,// long keepAliveTime, 超时3秒没有人调用就会释,放关闭窗口
                TimeUnit.SECONDS,// TimeUnit unit, 超时单位 秒
                new LinkedBlockingDeque<>(3),// 阻塞队列(候客区最多3人)
                Executors.defaultThreadFactory(),// 默认线程工厂
                // 4种拒绝策略之一:
                // 队列满了,尝试去和 最早的竞争,也不会抛出异常!
                new ThreadPoolExecutor.DiscardOldestPolicy());

        //队列满了,尝试去和最早的竞争,也不会抛出异常!
        try {
            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 90; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(
                            Thread.currentThread().getName()+" ok");
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }

}

4种拒绝策略

new ThreadPoolExecutor.AbortPolicy()
银行满了,还有人进来,不处理这个人的,抛出异常

new ThreadPoolExecutor.CallerRunsPolicy()
哪来的去哪里!比如你爸爸 让你去通知妈妈洗衣服,妈妈拒绝,让你回去通知爸爸洗

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

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

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

package com.znb.pool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Demo01 {
    public static void main(String[] args) {
        // 自定义线程池!工作 ThreadPoolExecutor
        
        // 最大线程到底该如何定义
        // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高! 
        // 2、IO 密集型 > 判断你程序中十分耗IO的线程, 
        // 比如程序 15个大型任务 io十分占用资源!
        // IO密集型参数(最大线程数)就设置为大于15即可,一般选择两倍
        
        // 获取CPU的核数
        System.out.println(
            Runtime.getRuntime().availableProcessors());// 8核
        
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,// int corePoolSize, 核心线程池大小
            	// int maximumPoolSize, 最大核心线程池大小 8核电脑就是8
                Runtime.getRuntime().availableProcessors(),
                3,// long keepAliveTime, 超时3秒没有人调用就会释放
                TimeUnit.SECONDS,// TimeUnit unit, 超时单位 秒 
                new LinkedBlockingDeque<>(3),// 阻塞队列(候客区最多3人)
                Executors.defaultThreadFactory(),// 默认线程工厂
            	// 4种拒绝策略之一:
            	// 队列满了,尝试去和 最早的竞争,也不会抛出异常!
                new ThreadPoolExecutor.DiscardOldestPolicy());  
        
        //队列满了,尝试去和最早的竞争,也不会抛出异常!
        try {
            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(
                        Thread.currentThread().getName()+" ok");
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

“怎么样学姐,对JUC是不是有一定的了解了”
“对啊学弟,你写的太棒了”学姐说道
在这里插入图片描述

“那剩下的过几天再讲,我现在很累了”
“没事学弟,学姐下面给你吃,学姐下面可好吃了”
“不行,我要回去睡觉了”

学姐天天缠着问我JUC,搞得我没有时间打游戏,无奈之下我写下JUC基础,要她自己去研究 下

注:以上故事纯属扯淡

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code_BinBin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值