JUC并发工具包
图
JUC是java.util.concurrent包的简称,在Java5添加,目的就是为了更好的支持高并发任务,让开发者进行多线程编程时减少竞争条件和死锁的问题,掌握JUC才能在高并发的场景拥有更多的处理方法

JUC

JUC是java.util.concurrent包的简称,在Java5添加,目的就是为了更好的支持高并发任务,让开发者进行多线程编程时减少竞争条件和死锁的问题,掌握JUC才能在高并发的场景拥有更多的处理方法

进程和线程

运行中的每一个程序就是一个进程,Java默认有两个线程(main,GC)

Java真的能开启线程吗

查看线程启动方法的源码发现,调用了一个native方法,也就是说,Java是开不了的,调用了C++语言方法(Java无法直接操作系统)

并发和并行

  • 并发:多个线程同时操作同一个资源
  • 并行:在多核CPU下多个线程可以同时执行(单核CPU是模拟的)

线程状态

public enum State {
    // 新建
    NEW,

    // 就绪
    RUNNABLE,

    // 阻塞
    BLOCKED,

    // 等待(一直等)
    WAITING,

    // 超时等待(限时等)
    TIMED_WAITING,

    // 终止
    TERMINATED;
}

wait和sleep区别

  • 来自不同的类
    • wait来自Object
    • sleep来自Thread
  • 关于锁的释放:wait会释放锁,sheep不会释放
  • 使用范围不同:wait只能在同步代码块中使用,sheep可以在任何地方使用
  • 捕获异常问题:wait不用捕获异常,sheep需要
// 正常的睡眠方式,使用工具类
// 睡1天
TimeUnit.DAYS.sleep(1);
// 睡3秒
TimeUnit.SECONDS.sleep(3);

管程(Monitor)

  • 管程在Java中是管理类的状态变量,让这个类是线程安全的
  • 管程提供了对共享资源的互斥访问和同步机制
  • 每个对象都有一个与之相关联的管程,可以使用synchronized关键字来实现对管程的加锁和解锁操作

Synchronized和Lock的区别

  • synchronized是关键字,Lock是一个Java类
  • synchronized无法获得锁的状态,Lock可以判断是否获取到了锁
  • synchronized会自动释放锁,Lock需要手动释放锁
  • synchronized为可重入锁,不可以中断,非公平;Lock默认也为可重入锁,可以判断锁,非公平(可设置)
  • synchronized适合少量的代码,Lock适合大量的同步代码

生产者消费者

传统方式

/**
 * 生产者消费者问题
 */
public class ProAndCon {
    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;

    /**
     * 生产
     */
    public synchronized void increment() throws InterruptedException {
        // 已有资源等待消费者消费后生产
        if (number != 0) {
            this.wait();
        }
        // 生产
        number++;
        System.out.println(Thread.currentThread().getName() + "-->" + number);
        // 通知消费者开始消费
        this.notify();
    }
    /**
     * 消费
     */
    public synchronized void decrement() throws InterruptedException {
        // 没有资源等待生产
        if (number == 0) {
            this.wait();
        }
        // 消费
        number--;
        System.out.println(Thread.currentThread().getName() + "-->" + number);
        // 通知生产者开始生产
        this.notify();
    }
}

注意:这里会出现问题,当两个以上线程运行时,就会出现虚假唤醒问题,这是由于if判断导致,解决办法为改为where判断

图

Lock实现

/**
 * 生产者消费者问题
 */
public class ProAndCon {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "B").start();
    }
}

/**
 * 资源
 */
class Data {
    private int number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    /**
     * 生产
     */
    public void increment() {
        // 加锁
        lock.lock();
        try {
            // 已有资源等待消费者消费后生产
            while (number != 0) {
                condition.await();
            }
            // 生产
            number++;
            System.out.println(Thread.currentThread().getName() + "-->" + number);
            // 通知其他线程
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
    /**
     * 消费
     */
    public void decrement() {
        // 加锁
        lock.lock();
        try {
            // 没有资源等待生产
            if (number == 0) {
                condition.await();
            }
            // 消费
            number--;
            System.out.println(Thread.currentThread().getName() + "-->" + number);
            // 通知生产者开始生产
            // 通知其他线程
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}

Condition精准唤醒

Condition的优势在于可以精准唤醒

/**
 * 生产者消费者问题
 */
public class ProAndCon {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.runA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.runB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.runC();
            }
        }, "C").start();
    }
}

/**
 * 资源
 */
class Data {
    // 标志位:1A,2B,3C
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    public void runA() {
        // 加锁
        lock.lock();
        try {
            // 不该自己执行就等待
            while (number != 1) {
                condition1.await();
            }
            // 执行完切换标志位
            number = 2;
            System.out.println(Thread.currentThread().getName());
            // 唤醒自定线程
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
    public void runB() {
        // 加锁
        lock.lock();
        try {
            // 不该自己执行就等待
            while (number != 2) {
                condition2.await();
            }
            // 执行完切换标志位
            number = 3;
            System.out.println(Thread.currentThread().getName());
            // 唤醒自定线程
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
    public void runC() {
        // 加锁
        lock.lock();
        try {
            // 不该自己执行就等待
            while (number != 3) {
                condition3.await();
            }
            // 执行完切换标志位
            number = 1;
            System.out.println(Thread.currentThread().getName());
            // 唤醒自定线程
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}

能实现类似生产线的线程