JUC笔记

本文详细介绍了Java并发编程中的关键概念,包括JUC(JavaUtilConcurrency)包,线程与进程的区别,线程的创建,以及Synchronized和Lock锁的使用。讨论了并发编程的本质,展示了生产者消费者问题的解决方案,并探讨了不同锁现象。此外,文章还提到了集合类在并发环境下的不安全问题以及解决方案,Callable接口与Runnable的区别,以及常用辅助类如CountDownLatch、CyclicBarrier和Semaphore的应用。最后,文章讨论了线程池的创建、参数配置及其拒绝策略。
摘要由CSDN通过智能技术生成

1、什么是JUC

JUC是java.util.concurrent包的简称。


2、线程和进程

1、线程和进程

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

Java本身  真的可以开启线程吗? 开不了。

通过private native void start0();开启的


 2、并行并发

并发(多线程操作同一资源):CPU一核 ,模拟出多条线程,快速交替。
并行(多人一起行走):CPU多核,多个线程可以同时执行;
并发编程的本质: 充分利用 CPU 的资源

3Lock锁(重点)

传统 Synchronized
public class SaleTicketDemo01 { // 卖票例子
    public static void main(String[] args) {
        // 并发:多线程操作同一资源类, 把资源类丢入线程
        Ticket ticket = new Ticket();
        // @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
        new Thread(() -> {
            for (int i = 1; i < 40; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 1; i < 40; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i < 40; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}



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

    // 卖票方式
    public synchronized void sale() { // 加上synchronized:本质: 队列,锁
//    public void sale() { // 未加synchronized出现冲突
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
        }
    }
}
Lock 接口
公平锁:十分公平:先来后到。
非公平锁:十分不公平:可插队 (默认)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
        Ticket2 ticket = new Ticket2();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
        new Thread(() -> {
            for (int i = 1; i < 40; i++) ticket.sale();
        }, "A").start();
        new Thread(() -> {
            for (int i = 1; i < 40; i++)
                ticket.sale();
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i < 40; i++)
                ticket.sale();
        }, "C").start();
    }
}

// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class Ticket2 {
    // 属性、方法
    private int number = 30;
    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock(); // 加锁
        try {
            // 业务代码
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 解锁
        }
    }
}
Synchronized Lock 区别
  1. Synchronized 是内置的Java关键字, Lock 是一个Java类。
  2. Synchronized 无法判断获取锁状态, Lock 可判断是否获取到锁
  3. Synchronized 会自动释放锁, lock 必须手动释放锁!如果不释放锁,产生 死锁。
  4. Synchronized 线程1(获得锁,阻塞)、线程2(傻等);Lock锁就不一定会等待下去;
  5. Synchronized 可重入锁,不可中断,非公平, Lock ,可重入锁,可判断锁,非公平(可设置);
  6. Synchronized 适合锁少量的代码同步问题, Lock 适合锁大量的同步代码!

4、生产者和消费者问题

生产者和消费者问题 Synchronized 

/**
 * 线程间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}



// 判断等待,业务,通知
class Data { // 数字 资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        if (number != 0) { //0
// 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number == 0) { // 1
// 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
        this.notifyAll();
    }
}
问题存在, A B C D 4 个线程! 虚假唤醒

 if 改成 while 判断

/**
 * 线程间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

// 判断等待,业务,通知
class Data2 { // 数字 资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        while (number != 0) { //0
// 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我+1完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) { // 1
// 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
        this.notifyAll();
    }
}

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

通过 Lock 找到 Condition

代码实现:

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

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

// 判断等待,业务,通知
class Data3 { // 数字 资源类
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //condition.await(); // 等待
    //condition.signalAll(); // 唤醒全部
    //+1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            // 业务代码
            while (number != 0) { //0
                // 等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // 通知其他线程,我+1完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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


用 Condition 优势: 精准的通知和唤醒线程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 业务:A 执行完调用B,B执行完调用C,C执行完调用A
 */
public class D {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

class Data3 { // 资源类 Lock
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1; // 1A 2B 3C

    public void printA() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            while (number != 1) {
                // 等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>AAA");
            // 唤醒,唤醒指定的人,B
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            // 唤醒,唤醒指定的人,c
            number = 3;
            condition3.signal();

        } catch (
                Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printC() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            // 业务,判断-> 执行-> 通知
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>CCC");
            // 唤醒,唤醒指定的人,c
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

58锁现象

8锁,就是关于锁的8个问题
1、标准情况下,两个线程先打印发短信?打电话?——发短信(先得到锁先执行)
2、发短信延迟3秒,两个线程先打印发短信?打电话?——发短信(先得到锁先执行)

3、增加一个普通方法,先执行发短信还是Hello? 普通方法Hello(没有锁,不是同步方法,不受锁影响)
4、两个对象,两个同步方法, 发短信还是 打电话?——打电话(两把锁,互不影响)

5、增加两个静态同步方法,只有一个对象,先打印 发短信?打电话?发短信(类一加载就有!锁的是Class)
6、两个对象!增加两个静态的同步方法, 先打印 发短信?打电话?发短信(类一加载就有!锁的是Class)

7、1个静态同步方法,1个普通同步方法 ,一个对象,先打印发短信?打电话?(类锁和对象锁是两把锁,互不影响)
8、1个静态同步方法,1个普通同步方法 ,两个对象,先打印发短信?打电话?(类锁和对象锁是两把锁,互不影响)

import java.util.concurrent.TimeUnit;

/**
 * 8锁,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话   发短信
 * 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话   发短信
 */
public class Test1 {
    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(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}
import java.util.concurrent.TimeUnit;

/**
 * 3、 增加了一个普通方法后!先执行发短信还是Hello? 普通方法Hello
 * 4、 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
 */
public class Test2 {
    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();
    }
}

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

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

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

/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?发短信
 * 6、两个对象!增加两个静态的同步方法, 先打印 发短信?打电话?发短信
 */
public class Test3 {
    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(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }
}
import java.util.concurrent.TimeUnit;

/**
 * 7、1个静态的同步方法,1个普通的同步方法 ,一个对象,先打印 发短信?打电话?打电话
 * 8、1个静态的同步方法,1个普通的同步方法 ,两个对象,先打印 发短信?打电话?打电话
 */
public class Test4 {
    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(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 普通的同步方法 锁的调用者
    public synchronized void call() {
        System.out.println("打电话");
    }
}
小结
new this 锁 具体的一个对象
static Class 锁 唯一的一个模板

6、集合类不安全

List 不安全

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class ListTest {
    public static void main(String[] args) {
        /*
         * 0、List<String> list = new ArrayList<>(); // 不安全,出现ConcurrentModificationException(并发修改异常异常)
         * 解决方案;
         * 1、List<String> list = new Vector<>();
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>()); // 通过工具类转成synchronized
         * 3、List<String> list = new CopyOnWriteArrayList<>(); JUC下解决方法:COW
         */
// CopyOnWrite(COW)写入时复制, 计算机程序设计领域的一种优化策略;
// 多个线程调用时,list,读取的是固定的,写入时出现覆盖(并发修改异常)
// 读写分离——在写入时避免覆盖,造成数据问题
// CopyOnWriteArrayList比Vector优势:Vector用synchronized处理,效率低。CopyOnWriteArrayList用lock。
        List<String> list = new Vector<>();
        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 不安全
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/*
 * 0、Set<String> set = new HashSet<>();不安全,同样会出现:ConcurrentModificationException
 * 解决方案;
 * 1、Set<String> set = Collections.synchronizedSet(new HashSet<>());
 * 2、Set<String> set = new CopyOnWriteArraySet<>(); JUC下解决方法:COW
 */
public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 80; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}
Map 不安全
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/*
 * 0、Map<String, String> map = new HashMap<>();不安全,出现:ConcurrentModificationException
 * 解决方案;
 * 1、Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
 * 2、Map<String, String> map = new ConcurrentHashMap<>(); JUC下解决方法:COW
 */
public class MapTest {
    public static void main(String[] args) {
        // 工作中不用 HashMap
        // 默认等价于new HashMap<>(16,0.75);
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

7Callable

 Callable与Rannable不同:

  1. 可以有返回值。
  2. 可以抛出异常。
  3. 方法不同,run() / call()。
代码测试
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable()).start(); 不用此方法1:Runnable没有返回值

        // 线程启动只有一种方式:new Thread().start(); 但不能直接调用Callable
        // new Thread(new FutureTask<V>()).start(); 和方法1等价,且FutureTask可以调用Callable
        // new Thread(new FutureTask<V>( Callable )).start();
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread); // 适配类
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start(); // 结果会被缓存,效率高
        Integer o = (Integer) futureTask.get(); //这个get方法会产生阻塞!把他放到最后或用异步通信来处理
        System.out.println(o);
    }
}

//class callable.MyThread implements Runnable {
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("call()"); // 只会打印1个call
        return 1024;
    }

//    @Override
//    public void run() {}
}
细节:
  1. 有缓存
  2. 获取结果可能需要等待,会阻塞

8、常用的辅助类

8.1CountDownLatch

import java.util.concurrent.CountDownLatch;

// 计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
// 总数是6,必须要执行任务的时候,再使用!
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " Go out");
                countDownLatch.countDown(); // 数量-1
            }, String.valueOf(i)).start();
        }
        countDownLatch.await(); // 等待计数器归零,然后再向下执行
        System.out.println("Close Door");
    }
}

原理:
countDownLatch.countDown();        // 数量-1
countDownLatch.await();        // 等待计数器归零,然后向下执行
每次有线程调用 countDown() ,  数量 -1 ,假设计数器变为 0 countDownLatch.await() 就会被唤醒,继续执行!

8.2CyclicBarrier

加法计数器

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

public class CyclicBarrierDemo {
    public static void main(String[] args) {
/*
 * 集齐7颗龙珠召唤神龙
 */
// 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤神龙成功!");
        });
        for (int i = 1; i <= 7; i++) {
            final int temp = i;
// lambda能操作到 i 吗
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "收集"+temp+" 个龙珠");
                try {
                    cyclicBarrier.await(); // 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3Semaphore

Semaphore:信号量

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

/*
抢车位!
6车---3个停车位置
*/
public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程数量:停车位! 限流!
        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();
        }
    }
}
原理:

semaphore.acquire():获得,假设如果已经满了,等待,等待被释放为止!

semaphore.release():释放,会将当前的信号量释放 + 1,然后唤醒等待的线程! 作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!


9、读写锁


10、阻塞队列

阻塞队列:


BlockingQueue BlockingQueue 不是新的东西

什么情况下会使用阻塞队列:多线程并发处理,线程池!


学会使用队列
添加、移除
四组 API

 

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

public class Test {
    public static void main(String[] args) throws InterruptedException {
//        test1();
//        test2();
        test3();
    }

    /*
     * 抛出异常
     */
    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.add("d")); // IllegalStateException: Queue full 抛出异常!
        System.out.println(blockingQueue.element()); // 查看队首元素
        System.out.println(blockingQueue.peek()); // 查看队首元素
        System.out.println("=============");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        // System.out.println(blockingQueue.remove()); // java.util.NoSuchElementException 抛出异常!
    }

    /*
     * 有返回值,没有异常
     */
    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.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 不抛出异常!
    }

    /*
     * 等待,阻塞(一直阻塞)
     */
    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()); // 没有这个元素,一直阻塞
    }

    /*
     * 等待,阻塞(等待超时)
     */
    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 同步队列
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/*
 * 同步队列
 * 和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
 * put一个元素,必须从里面先take取出来,否则不能put进去值
 */
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");
                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 {
                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();
    }
}

11、线程池(重点)

线程池:三大方法、 7 大参数、 4 种拒绝策略
池化技术
程序的运行本质:占用系统的资源
优化资源的使用  =>  池化技术
线程池、连接池、内存池、对象池 ... 创建、销毁。十分浪费资源
池化技术:事先准备好一些资源,有人要用,就直接拿,用完之后还给线程池。
线程池的好处 :
  1. 降低资源的消耗
  2. 提高响应的速度
  3. 方便管理
线程复用、可控制最大并发数、管理线程

 线程池:三大方法

代码:

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

// Executors 工具类、3大方法
public class Demo01 {
    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
        // ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
        ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩,遇强则强,遇弱则弱
        try {
            for (int i = 0; i < 100; i++) {
//                new Thread().start();
                // 用线程池后,用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

7 大参数
源码分析:
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(5, 5,
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 handle // 拒绝策略) {
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;
}

手动创建线程池
import java.util.concurrent.*;

// Executors 工具类、3大方法
public class Demo02 {
    public static void main(String[] args) {
        // 自定义线程池!工作用 ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3), // 候客区
                Executors.defaultThreadFactory(), // 默认线程工程
//                new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略:队列满了,不处理,抛出异常(默认)
//                new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略:队列满了,哪里的去哪里,main
//                new ThreadPoolExecutor.DiscardPolicy()); // 拒绝策略:队列满了,丢失任务不执行,不会抛出异常
                new ThreadPoolExecutor.DiscardOldestPolicy()); // 拒绝策略:队列满了,尝试去和最早的竞争,也不会抛出异常!
        try {
            // 最大承载:Deque + max:3+5 超过最大承载 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();
        }
    }
}
4 种拒绝策略
/**
* new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人进来,不处理这个人的,抛异常
* new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不抛异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,不会
抛异常
*/
小结和拓展
池的最大的大小如何设置
了解: IO 密集型, CPU 密集型:(调优)

代码实例:

import java.util.concurrent.*;

public class Demo03 {
    public static void main(String[] args) {
        // 自定义线程池!工作 ThreadPoolExecutor
        // 最大线程到底该如何定义
        // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
        // 2、IO 密集型 > 判断你程序中十分耗IO的线程,
        // 程序 15个大型任务 设置30个

        // 获取CPU的核数
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                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();
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王雀跃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值