JUC ~ java.util.concurrent。

JUC ~ java.util.concurrent。



JUC ~ java.util.concurrent。

java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks

concurrent

a. 并存的;同时发生的

https://docs.oracle.com/javase/8/docs/api/

在这里插入图片描述

  • 普通的线程 Thread。
  • Runnable。
    没有返回值,效率相比 Callable 相对较低。


线程 & 进程。

在这里插入图片描述

  • Java 默认有几个线程?

2 个。main 和 gc。

  • 理解。

一个进程 Typora。输入 + 自动保存(线程负责的)(两个线程)。

对于 Java 而言,Java 不能直接开启线程。而是 native 方法让 C++ 操作底层系统开启线程。Java 不能操作硬件。

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();



并发 & 并行。

并发编程,并发 & 并行。

  • 并发(多线程操作同一资源)。

CPU 一核,模拟出来多条线程,天下武功,唯快不破,快速交替。

  • 并行(多个人一起行走)。

CPU 多核,多个可线程以同时执行。

package com.geek.demo00;

/**
 * @author geek
 */
public class Test01 {

    public static void main(String[] args) {

        // 获取 CPU 核数。
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        System.out.println(availableProcessors);// 8
    }

}



并发编程的本质:充分利用 CPU 资源。


线程的状态。

  • NEW // 新生。
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED

    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(); vs. sleep();。
  • 来自不同类。

wait(); ~ Object。
sleep(); ~ Thread。

import java.util.concurrent.TimeUnit;

		try {
            TimeUnit.DAYS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
  • 锁的释放。

wait(); 会释放锁。
sleep(); 睡觉,抱着锁睡觉,不释放。企业中一般不用。

  • 使用的范围不同。

sleep(); 可以在任何地方睡。
wait(); 必须在同步代码块中。

  • 是否需要捕获异常。
    ×××

wait(); 不需要。
sleep(); 需要。



Lock 锁。(重点)。

传统 ~ synchronized。
package com.geek.demo01;

/**
 * 真正的多线程开发,公司中的开发。降低耦合性。
 * 线程就是一个单独的资源类,没有任何附属的操作。
 * - 属性、方法。
 *
 * @author geek
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {

        // 并发。多线程(new Thread())操作同一个资源类(Ticket)。把资源类丢入线程。
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "C").start();
    }

}


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

    // 卖票的方法。
    synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + " 卖出了第 " + (30 - number + 1) + " 张票,剩余 " + (number-- - 1));
        }
    }
}

// synchronized 本质:排队,队列。锁。

// 加 synchronized。
/*
A 卖出了第 1 张票,剩余 29
A 卖出了第 2 张票,剩余 28
A 卖出了第 3 张票,剩余 27
A 卖出了第 4 张票,剩余 26
A 卖出了第 5 张票,剩余 25
A 卖出了第 6 张票,剩余 24
A 卖出了第 7 张票,剩余 23
B 卖出了第 8 张票,剩余 22
B 卖出了第 9 张票,剩余 21
B 卖出了第 10 张票,剩余 20
B 卖出了第 11 张票,剩余 19
B 卖出了第 12 张票,剩余 18
B 卖出了第 13 张票,剩余 17
B 卖出了第 14 张票,剩余 16
B 卖出了第 15 张票,剩余 15
B 卖出了第 16 张票,剩余 14
B 卖出了第 17 张票,剩余 13
A 卖出了第 18 张票,剩余 12
A 卖出了第 19 张票,剩余 11
A 卖出了第 20 张票,剩余 10
A 卖出了第 21 张票,剩余 9
A 卖出了第 22 张票,剩余 8
A 卖出了第 23 张票,剩余 7
A 卖出了第 24 张票,剩余 6
A 卖出了第 25 张票,剩余 5
A 卖出了第 26 张票,剩余 4
A 卖出了第 27 张票,剩余 3
A 卖出了第 28 张票,剩余 2
A 卖出了第 29 张票,剩余 1
A 卖出了第 30 张票,剩余 0

Process finished with exit code 0

 */

// 不加 synchronized。
/*
A 卖出了第 1 张票,剩余 29
A 卖出了第 2 张票,剩余 28
A 卖出了第 3 张票,剩余 27
A 卖出了第 4 张票,剩余 26
A 卖出了第 5 张票,剩余 25
A 卖出了第 7 张票,剩余 23
B 卖出了第 6 张票,剩余 24
B 卖出了第 9 张票,剩余 21
B 卖出了第 10 张票,剩余 20
B 卖出了第 11 张票,剩余 19
B 卖出了第 12 张票,剩余 18
B 卖出了第 14 张票,剩余 16
B 卖出了第 15 张票,剩余 15
A 卖出了第 8 张票,剩余 22
B 卖出了第 16 张票,剩余 14
C 卖出了第 13 张票,剩余 17
B 卖出了第 18 张票,剩余 12
A 卖出了第 17 张票,剩余 13
B 卖出了第 20 张票,剩余 10
B 卖出了第 22 张票,剩余 8
B 卖出了第 23 张票,剩余 7
B 卖出了第 24 张票,剩余 6
B 卖出了第 25 张票,剩余 5
B 卖出了第 26 张票,剩余 4
B 卖出了第 27 张票,剩余 3
C 卖出了第 19 张票,剩余 11
C 卖出了第 29 张票,剩余 1
C 卖出了第 30 张票,剩余 0
B 卖出了第 28 张票,剩余 2
A 卖出了第 21 张票,剩余 9

Process finished with exit code 0
 */

/*
public class SaleTicketDemo01 {

    public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }

}

class MyThread implements Runnable {
    @Override
    public void run() {

    }
}

*/



Lock。

实现类。

  • ReentrantLock ~ 可重入锁。
  • ReentrantLockWriteLock.ReadLock ~ 读锁。
  • ReentrantLockWriteLock.WriteLock ~ 写锁。

在这里插入图片描述

  • ReentrantLock。(常用)。

公平锁(先来后到) & 非公平锁(可以插队,默认)。

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }



ReentrantLock。
package com.geek.demo01;

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

/**
 * 真正的多线程开发,公司中的开发。降低耦合性。
 * 线程就是一个单独的资源类,没有任何附属的操作。
 * + 属性、方法。
 */
public class SaleTicketDemo02 {
    public static void main(String[] args) {

        // 并发。多线程(new Thread())操作同一个资源类(Ticket)。把资源类丢入线程。
        Ticket2 ticket = new Ticket2();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "C").start();
    }

}


class Ticket2 {

    private Lock lock = new ReentrantLock();

    // 属性、方法。
    private int number = 30;

    // 卖票的方法。
    public void sale() {

        lock.lock();// 加锁。

        try {
            // 业务代码。
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + (30 - number + 1) + " 张票,剩余 " + (number-- - 1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();// 解锁。
        }
    }

}



synchronized v.s. Lock。
  • synchronized Java 内置关键字。
    Lock 是一个 Java 类。

  • synchronized 无法判断获取锁的状态。
    Lock 可以判断是否获取到了锁。

  • synchronized 会自动释放锁。
    Lock 必须手动释放锁。如果不释放锁会死锁。

  • synchronized 线程 1 (获得锁,阻塞),线程 2 (等待,傻傻的等)。
    Lock 锁就不会一直等待下去。lock.tryLock();。

  • synchronized 可重入锁,不可以中断,非公平。
    Lock 可重入锁,可以判断锁,非公平(可以自己设置)。

  • synchronized 适合锁少量的代码同步问题。
    Lock 适合锁大量的同步代码。



生产者 & 消费者。

synchronized 版本。
package com.geek.pc;

/**
 * 线程之间的通信问题。生产者和消费者问题。
 * 线程交替进行。A B 操作同一个变量。
 * 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 {

    // 保持 number 为 0.
    private int number = 0;

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

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

  • 问题:2 个线程没问题,3 4 个线程就不行。

原因。

if 只有一次判断,并且 notifyAll();。

在这里插入图片描述

if 判断改为 while() {}。

package com.geek.pc;

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

/**
 * @author geek
 */
public class B {

    public static void main(String[] args) {
        Data2 data2 = new Data2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.increment();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.decrement();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.increment();
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.decrement();
            }
        }, "D").start();
    }

}

/**
 * 资源类。数字。
 */
// 判断等待 + `业务` + 通知。
class Data2 {

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();
//    condition.await();// 等待。
//    condition.signalAll();// 唤醒全部。

    // 保持 number 为 0.
    private int number = 0;

    // +1。
    public void increment() {

        lock.lock();

        // 业务代码。
        try {
//        if (number != 0) {
            while (number != 0) {
                // 等待。
                condition.await();
            }
            number++;// number 为 0,才 +1。
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // 通知其他线程,我 +1 完毕了。
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // -1。
    public void decrement() {

        lock.lock();

        // 业务代码。
        try {
//        if (number == 0) {
            while (number == 0) {
                // 等待。
                condition.await();
            }
            number--;// number 不为 0,才 -1。
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // 通知其他线程,我 -1 完毕了。
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}



juc 版。

在这里插入图片描述

package com.geek.pc;

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

/**
 * @author geek
 */
public class B {

    public static void main(String[] args) {
        Data2 data2 = new Data2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.increment();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.decrement();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.increment();
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data2.decrement();
            }
        }, "D").start();
    }

}

/**
 * 资源类。数字。
 */
// 判断等待 + `业务` + 通知。
class Data2 {

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();
//    condition.await();// 等待。
//    condition.signalAll();// 唤醒全部。

    // 保持 number 为 0.
    private int number = 0;

    // +1。
    public void increment() {

        lock.lock();

        // 业务代码。
        try {
//        if (number != 0) {
            while (number != 0) {
                // 等待。
                condition.await();
            }
            number++;// number 为 0,才 +1。
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // 通知其他线程,我 +1 完毕了。
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // -1。
    public void decrement() {

        lock.lock();

        // 业务代码。
        try {
//        if (number == 0) {
            while (number == 0) {
                // 等待。
                condition.await();
            }
            number--;// number 不为 0,才 -1。
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // 通知其他线程,我 -1 完毕了。
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}



Condition 精确唤醒。

eg. 生产线:下单 -> 支付 -> 交易 -> 物流。

package com.geek.pc;

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 C {

    public static void main(String[] args) {

        Data3 data3 = new Data3();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printC();
            }
        }, "C").start();
    }

}


/**
 * 资源类 Lock。
 */
class Data3 {

    private Lock lock = new ReentrantLock();

    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    private int number = 1;// 1 ~ A 2 ~ B 3 ~ C。

    public void printA() {
        lock.lock();
        try {
            // 业务,判断,执行,通知。
            while (number != 1) {
                // 等待。
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + " ~ AAAAA");
            // 唤醒。唤醒指定的人 ~ 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() + " ~ BBBBB");
            // 唤醒。唤醒指定的人 ~ 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() + " ~ CCCCC");
            // 唤醒。唤醒指定的人 ~ A。
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}



八锁现象。

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 *
 * - 标准情况下,先 1 ~ 发短信 后 2 ~ 打电话。
 * - sendSms(); 延迟 4 秒。先 1 ~ 发短信 后 2 ~ 打电话。(锁的存在)。synchronized 锁的对象是调用者。两个方法用的是同一个锁(Phone 对象)。谁先拿到谁先执行。
 */
public class Test01 {

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.senSms(), "A").start();

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

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

}


class Phone {

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

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

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 3. 增加了一个普通方法 hello(),先执行 hello();。(没有锁,不受锁的影响)。
 * hello
 * 发短信。
 */
public class Test02 {

    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        // 锁的存在。
        new Thread(() -> phone.senSms(), "A").start();

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

        new Thread(() -> phone.hello(), "B").start();
    }

}


class Phone2 {

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

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

    // 这里没有锁。
    void hello() {
        System.out.println("hello");
    }

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 4. 两个对象,两个同步方法。
 * 打电话。
 * 发短信。
 */
public class Test03 {

    public static void main(String[] args) {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        // 锁的存在。
        new Thread(() -> phone1.senSms(), "A").start();

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

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

}


class Phone3 {

    synchronized void senSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信。");
    }

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

    // 这里没有锁。
    void hello() {
        System.out.println("hello");
    }

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 5. 两个静态同步方法。只有一个对象。
 * 打电话。
 * 发短信。
 *
 * - 6. 两个对象。两个静态同步方法。
 */
public class Test04 {

    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        // 锁的存在。
        new Thread(() -> phone.senSms(), "A").start();

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

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

}


class Phone4 {

    // synchronized 锁的对象是调用者。
    // static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
    // Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;

    static synchronized void senSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信。");
    }

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

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 5. 两个静态同步方法。只有一个对象。
 * 打电话。
 * 发短信。
 * <p>
 * - 6. 两个对象。两个静态同步方法。
 * 发短信。
 * 打电话。
 */
// synchronized 锁的对象是调用者。
// static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
// Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;
public class Test05 {

    public static void main(String[] args) {
        Phone5 phone1 = new Phone5();
        Phone5 phone2 = new Phone5();
        // 锁的存在。
        new Thread(() -> phone1.senSms(), "A").start();

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

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

}


class Phone5 {

    // synchronized 锁的对象是调用者。
    // static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
    // Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;

    static synchronized void senSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信。");
    }

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

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 7. 一个静态同步方法,一个普通同步方法。一个对象。
 * 打电话。
 * 发短信。
 */
// synchronized 锁的对象是调用者。
// static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
// Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;
public class Test05 {

    public static void main(String[] args) {
        Phone5 phone = new Phone5();
        // 锁的存在。
        new Thread(() -> phone.senSms(), "A").start();

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

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

}


class Phone5 {

    // synchronized 锁的对象是调用者。
    // static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
    // Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;

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

    /**
     * 普通同步方法。锁的是调用者。
     */
    synchronized void call() {
        System.out.println("打电话。");
    }

}

package com.geek.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8 锁,就是关于锁的 8 个问题。
 * <p>
 * - 7. 一个静态同步方法,一个普通同步方法。两个对象。
 * 打电话。
 * 发短信。
 * (锁不同)。
 */
// synchronized 锁的对象是调用者。
// static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
// Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;
public class Test06 {

    public static void main(String[] args) {
        Phone6 phone1 = new Phone6();
        Phone6 phone2 = new Phone6();
        // 锁的存在。
        new Thread(() -> phone1.senSms(), "A").start();

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

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

}


class Phone6 {

    // synchronized 锁的对象是调用者。
    // static 静态方法,类一加载就有了。synchronized 锁的是 Class (模板)。
    // Phone4.class 唯一的 Class 对象(模板)。
//    Class<Phone3> aClass = Phone3.class;

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

    /**
     * 普通同步方法。锁的是调用者。
     */
    synchronized void call() {
        System.out.println("打电话。");
    }

}



集合类不安全。

List。
package com.geek.unsafe;

import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

public class UnsafeListTest {

    public static void main(String[] args) {
        // 单线程没毛病。
//        List<String> list = Arrays.asList("1", "2", "3");
//        list.forEach(System.out::println);

//        List<String> list = new ArrayList<>();
        // 并发下 ArrayList 是不安全的。

        // 解决:
// 1.       List<String> list = new Vector<>();
// 2.       List<Object> synchronizedList = Collections.synchronizedList(new ArrayList<>());
        // 3. 写入时复制。COW 计算机程序设计领域的一种优化策略。
        // 一般,多个线程调用时,list 读取时固定。写入时覆盖。
        // CopyOnWriteArrayList 在写入时避免覆盖造成数据问题。(写入时先复制一份数据,写入完成再插入数据到 list)。
        CopyOnWriteArrayList<Object> list = new CopyOnWriteArrayList<>();

        // java.util.ConcurrentModificationException
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println("list = " + list);
            }, String.valueOf(i)).start();
        }
    }

}

package com.geek.unsafe;

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

public class SetTest {

    public static void main(String[] args) {
//        Set<String > set = new HashSet<>();
//        Set<Object> synchronizedSet = Collections.synchronizedSet(new HashSet<>());

        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println("set = " + set);
            }, String.valueOf(i)).start();
        }
    }

}

HashSet 底层 ~ HashMap。
    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
  • add();。

Set 本质就是 map,key 是不重复的。

    /**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }


    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();


HashMap。

package com.geek.unsafe;

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

public class MapTest {

    public static void main(String[] args) {
        // 工作中不这样用。
//        HashMap<String, String> hashMap = new HashMap<>();

        // 加载因子。初始化容量。new HashMap<>(16, 0.75);
//        Map<String, String> hashMap = new HashMap<>();

        Map<String, Object> hashMap = new ConcurrentHashMap<>();

        // java.util.ConcurrentModificationException
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(hashMap);
            }, String.valueOf(i)).start();
        }
    }

}



Callable。

在这里插入图片描述
可以有返回值。

可以抛出异常。

方法不同。run(); / call();。

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

在这里插入图片描述

package com.geek.callable;

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() {
//            @Override
//            public void run() {
//
//            }
//        }).start();

//        new Thread(new FutureTask<V>()).start();
//        new Thread(new FutureTask<V>(Callable<V>)).start();

        MyThread myThread = new MyThread();
        // 适配类。
        FutureTask futureTask = new FutureTask(myThread);
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();// 结果会被缓存。效率高。
        // 获取 Callable 返回结果。
        Object o = futureTask.get();// get(); 方法可能会阻塞。把 ta 放到最后或使用异步处理。
        System.out.println("o = " + o);
    }

}


class MyThread implements Callable<Integer> {

    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    @Override
//    public Object call() throws Exception {
    public Integer call() throws Exception {
//        return null;
        return 1024;
    }

}



常用辅助类。

CountDownLatch。

latch

n. 门闩;插销;碰锁;弹簧锁
v. 用插销插上;用碰锁锁上

package com.geek.add;

import java.util.concurrent.CountDownLatch;

/**
 * countDown(); 数量 -1。
 * 就为 0,countDownLatch.await(); 被唤醒,继续以下代码。
 */
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        // 总数是 6。
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " went out.");
                countDownLatch.countDown();// -1。
            }, String.valueOf(i)).start();
        }
        // 等待计数器归零,再向下执行。
        countDownLatch.await();

        System.out.println("Door closed.");
    }

}

/*
0 went out.
3 went out.
1 went out.
2 went out.
5 went out.
4 went out.
Door closed.

Process finished with exit code 0
 */


CyclicBarrier。

理解:加法计数器。

cyclic

a. 循环的;周期的

barrier

n. 屏障;障碍物;障碍;阻力;关卡;分界线;隔阂

package com.geek.add;

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 = 0; 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。

n. 信号标;旗语
v. 打旗语;(用其他类似的信号系统)发信号

package com.geek.add;

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

public class SemaphoreDemo {

    public static void main(String[] args) {
        // 参数:线程数量。停车位。
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; 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();
        }

    }

}



读写锁。

interface ReadWriteLock。

public interface ReadWriteLock
A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.

读可以被多线程同时读,写只能一个线程去写。

package com.geek.rw;

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

/**
 * 独占锁(写锁)~ 一次只能被一个线程占有。
 * 共享锁(读锁)~ 一次可以被多个线程占有。
 * ReadWriteLock
 * 读 ~ 读 可以共存。
 * 读 ~ 写 不能共存。
 * 写 ~ 写 不能共存。
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCacheLock myCacheLock = new MyCacheLock();

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

        // 读取。
        for (int i = 0; i < 5; i++) {
            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();

    /**
     * 存。
     *
     * @param key
     * @param value
     */
    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();
        }
    }

    /**
     * 取。
     *
     * @param key
     */
    public void get(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 读取 " + key);
            Object o = map.get(key);
            System.out.println("o = " + o);
            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<>();

    /**
     * 存。
     *
     * @param key
     * @param value
     */
    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");

    }

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

}



阻塞队列。

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加add();offer();put();offer( , , );
移除remove();poll();take();poll( , );
检测队首元素element();peek();--
package com.geek.queue;

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

public class QueueTest {

    public static void main(String[] args) throws InterruptedException {
        test4();
    }

    /**
     * 抛出异常。
     */
    private static void test1() {
        // 队列的大小。
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));// true.
        // Exception in thread "main" java.lang.IllegalStateException: Queue full
//        System.out.println(arrayBlockingQueue.add("d"));

        System.out.println(arrayBlockingQueue.element());// 队首。a
        System.out.println("~ ~ ~ ~ ~ ~ ~");

        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        // Exception in thread "main" java.util.NoSuchElementException
//        System.out.println(arrayBlockingQueue.remove());
    }

    /**
     * 不抛异常。
     */
    private static void test2() {
        // 队列的大小。
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.offer("a"));// true
        System.out.println(arrayBlockingQueue.offer("b"));// true
        System.out.println(arrayBlockingQueue.offer("c"));// true
        System.out.println(arrayBlockingQueue.offer("d"));// false,不抛异常。

        System.out.println(arrayBlockingQueue.peek());// 队首。a
        System.out.println("~ ~ ~ ~ ~ ~ ~");

        System.out.println(arrayBlockingQueue.poll());// a
        System.out.println(arrayBlockingQueue.poll());// b
        System.out.println(arrayBlockingQueue.poll());// c
        System.out.println(arrayBlockingQueue.poll());// null,不抛异常。
    }

    /**
     * 等待,阻塞。一直阻塞。
     */
    private static void test3() throws InterruptedException {
        // 队列的大小。
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
        // 队列没有位置了,一直阻塞。
//        arrayBlockingQueue.put("d");

        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        // 队列没有元素了,一直阻塞。
//        System.out.println(arrayBlockingQueue.take());
    }

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

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

        System.out.println("~ ~ ~ ~ ~ ~ ~");

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // 等待,超过 2 秒退出。
        System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
    }

}



同步队列 ~ SynchronousQueue。容量为 1。

进去一个元素,必须等待取出来之后,才能再放一个元素。

put、take。

package com.geek.queue;

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

/**
 * 同步队列。
 * 进去一个元素,必须等待取出来之后,才能再放一个元素。
 * <p>
 * put、take。
 */
public class SynchronousQueueDemo {

    public static void main(String[] args) {
        SynchronousQueue synchronousQueue = new SynchronousQueue();

        new Thread(() -> {
            try {
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 1.");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " put 2.");
                synchronousQueue.put("3");
                System.out.println(Thread.currentThread().getName() + " put 3.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }

}



线程池。

程序的运行,本质:占用系统资源。

线程池、连接池、内存池、对象池,创建、销毁十分浪费资源。

↓ ↓ ↓

池化技术。

事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

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

线程复用、控制最大并发数、管理线程。

3 大方法。
package com.geek.pool;

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

/**
 * Executors 工具类。
 * 3 大方法。
 * 使用线程池创建线程。
 */
public class Demo01 {

    public static void main(String[] args) {
//        ExecutorService executorService = Executors.newSingleThreadExecutor();// 单个线程。
//        ExecutorService executorService = Executors.newFixedThreadPool(5);// 创建固定大小的线程池。
        ExecutorService executorService = Executors.newCachedThreadPool();// 可伸缩的,遇强则强,遇弱则弱。

        try {
            for (int i = 0; i < 100; i++) {
                executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池。
            executorService.shutdown();
        }
    }

}

〖强制〗线程池不允许使用 Executors 去创建, 而是通过ThreadPooIExecutor 的方式, 这样的处理方式让写的同学更加明确线程池的运行规则, 规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:

1 ) FixedThreadPool 和SingleThreadPooI:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2 ) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

  • 开启线程本质 ~ new ThreadPoolExecutor();。

int corePoolSize,// 核心线程池大小。
int maximumPoolSize,// 最大线程池大小。

都为 1。

    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue, and uses the provided ThreadFactory to
     * create a new thread when needed. Unlike the otherwise
     * equivalent {@code newFixedThreadPool(1, threadFactory)} the
     * returned executor is guaranteed not to be reconfigurable to use
     * additional threads.
     *
     * @param threadFactory the factory to use when creating new
     * threads
     *
     * @return the newly created single-threaded Executor
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

int corePoolSize,// 核心线程池大小。
int maximumPoolSize,// 最大线程池大小。

为传入的参数。

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

int corePoolSize,// 核心线程池大小。
int maximumPoolSize,// 最大线程池大小。

0 和 21 亿(2147483647)。

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
  • 本质 ~ public ThreadPoolExecutor();。
    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<hr>

<br>
     *         {@code corePoolSize < 0}<hr>

<br>
     *         {@code keepAliveTime < 0}<hr>

<br>
     *         {@code maximumPoolSize <= 0}<hr>

<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    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 大参数。


4 种拒绝策略。

在这里插入图片描述
在这里插入图片描述
一般 1 2 窗口开启,3 4 5 窗口关闭。

第三个人进来,进入等待区。

等待区满了,3 4 5 窗口人员回来上班。

等待区满了,又进来人了,拒绝策略生效。

  • 自己实现线程池。
package com.geek.pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Executors 工具类。
 * 3 大方法。
 * 使用线程池创建线程。
 */
public class Demo01 {

    public static void main(String[] args) {
        // 自定义线程池。
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,// 超时,1 个小时也没有人来 3 4 5 窗口,就关闭。
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()// 银行满了,但还有人进来,就不处理了,抛异常。
        );

        try {
            // 5 个人进入,只有 corePoolSize = 2 个线程处理。
            // 6 个人进入,最多有 3 个线程处理。
            // 7 个人进入,最多有 4 个线程处理。
            // 8 个人进入,最多有 5 个线程处理。
            // 9 个人进入,抛异常。
            // java.util.concurrent.RejectedExecutionException: Task com.geek.pool.Demo01$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池。
            threadPoolExecutor.shutdown();
        }
    }

}

package com.geek.pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Executors 工具类。
 * 3 大方法。
 * 使用线程池创建线程。
 */
public class Demo01 {

    public static void main(String[] args) {
        // 自定义线程池。
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,// 超时,1 个小时也没有人来 3 4 5 窗口,就关闭。
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.AbortPolicy()// 银行满了,但还有人进来,就不处理了,抛异常。
//                new ThreadPoolExecutor.CallerRunsPolicy()// 哪来的去哪里。(main(); 线程执行的。)
                // pool-1-thread-1
                //pool-1-thread-5
                //pool-1-thread-4
                //main
                //pool-1-thread-3
                //pool-1-thread-2
                //pool-1-thread-4
                //pool-1-thread-5
                //pool-1-thread-1
//                new ThreadPoolExecutor.DiscardPolicy()// 队列满了,不会抛出异常,丢掉任务(只执行了 8 次)。
                new ThreadPoolExecutor.DiscardOldestPolicy()// 队列满了,尝试和最早的线程竞争,也不抛出异常。
        );

        try {
            // 5 个人进入,只有 corePoolSize = 2 个线程处理。
            // 6 个人进入,最多有 3 个线程处理。
            // 7 个人进入,最多有 4 个线程处理。
            // 8 个人进入,最多有 5 个线程处理。
            // 9 个人进入,抛异常。
            // java.util.concurrent.RejectedExecutionException: Task com.geek.pool.Demo01$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池。
            threadPoolExecutor.shutdown();
        }
    }

}



池的大小该如何定义。
CPU 密集型。

CPU 几核就定义最大线程数为 CPU 核数。可以保持 CPU 效率最高。

Runtime.getRuntime().availableProcessors();



IO 密集型。

程序有 15 个大型任务,io 十分占资源。

判断程序中十分耗 io 资源的线程数,大于这个线程数即可。



四大函数式接口(重点)。

函数式接口 ~ @FunctionalInterface。
/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang;

/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   JDK1.0
 */
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

  • 自定义工具。
package com.geek.function;

import java.util.function.Function;

/**
 * Function 函数型接口。
 */
public class FunctionDemo {

    /*
    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }
    传入 T,返回 R。
     */

    public static void main(String[] args) {
//        Function<String, String> function = new Function<String, String>() {
//            /**
//             * Applies this function to the given argument.
//             *
//             * @param o the function argument
//             * @return the function result
//             */
//            @Override
//            public String apply(String s) {
//                return s;
//            }
//        };
        // 简化 ~ Lambda。
        Function function = (string) -> string;
        // 输出输入的值。
        System.out.println(function.apply("123"));
    }

}

package com.geek.function;

import java.util.function.Predicate;

/**
 * 断定型接口。有一个输入参数,返回值只能是 boolean。
 */
public class PredicateDemo{

    /*
    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
     */

    public static void main(String[] args) {
        // 判断字符串是否为空。
//        Predicate<String> predicate = new Predicate<String>() {
//            /**
//             * Evaluates this predicate on the given argument.
//             *
//             * @param s the input argument
//             * @return {@code true} if the input argument matches the predicate,
//             * otherwise {@code false}
//             */
//            @Override
//            public boolean test(String s) {
//                return s.isEmpty();
//            }
//        };

        // 简化。
//        Predicate<String> predicate = str -> str.isEmpty();
        Predicate<String> predicate = String::isEmpty;

        System.out.println(predicate.test("null"));// false
        System.out.println(predicate.test(""));// true
    }

}

package com.geek.function;

import java.util.function.Consumer;

/**
 * 消费型接口。只有输入,没有返回值。
 */
public class ConsumerDemo {

    /*
    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
     */

    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            /**
//             * Performs this operation on the given argument.
//             *
//             * @param o the input argument
//             */
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };

        Consumer<String> consumer = System.out::println;

        consumer.accept("abc");
    }

}

package com.geek.function;

import java.util.function.Supplier;

/**
 * 供给型接口。没有参数。只有返回值。
 */
public class SupplyDemo {

    /*
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    */

    public static void main(String[] args) {
//        Supplier<Integer> supplier = new Supplier<Integer>() {
//            /**
//             * Gets a result.
//             *
//             * @return a result
//             */
//            @Override
//            public Integer get() {
//                return 1024;
//            }
//        };

        Supplier<Integer> supplier = () -> 1024;

        System.out.println(supplier.get());
    }

}



Lambda 表达式。


链式编程。


Stream 流式计算。

集合、MySQL 本质是用来存储东西的。

计算都应该交给流来操作。

package com.geek.stream;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * 5 个用户。筛选。
 * id 是偶数。
 * 年龄大于 23。
 * 用户名转为大写字母。
 * 用户按名称倒序。
 * 只输出一个用户。
 */
public class Test {

    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(6, "e", 25);

        // 集合就是用来存储的。
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);

        // 计算交给 Stream 流。
//        Stream<User> stream = list.stream();
        list.stream()
                .filter(u -> u.getId() % 2 == 0)
                .filter(u -> u.getAge() > 23)
                .map(u -> u.getName().toUpperCase())
//                .sorted(new Comparator<String>() {
//                    @Override
//                    public int compare(String o1, String o2) {
//                        return o2.compareTo(o1);
//                    }
//                })
                .sorted(Comparator.reverseOrder())
                .limit(1)
                .forEach(System.out::println);
    }

}



ForkJoin。

jdk 1.7。并行执行任务。提高效率,大数据量。

大数据:Map Reduce(把大任务拆成小任务)。

在这里插入图片描述
ForkJoin 特点:工作窃取。

这里面维护的是双端队列。

在这里插入图片描述

package com.geek.forkjoin;

import java.util.concurrent.RecursiveTask;

/**
 * 求和计算。
 * 普通程序员
 * ForkJoin
 * Stream
 */
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;
    }

    /**
     * 计算方法。
     * The main computation performed by this task.
     *
     * @return the result of the computation
     */
    @Override
    protected Long compute() {
        if (end - start < temp) {
            Long sum = 0L;
            for (Long i = 1L; i <= 1_000_000_000; i++) {
                sum += i;
            }
            return sum;
        } else {// ForkJoin。
            long middle = (start + end) / 2;// 中间值。
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork();// 拆分任务,把任务压入线程队列。
            ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end);
            task2.fork();// 拆分任务,把任务压入线程队列。
            return task1.join() + task2.join();
        }
    }

//    public static void main(String[] args) {
//        int sum = 0;
//        for (int i = 1; i <= 10_000_000; i++) {
//            sum += i;
//        }
//        System.out.println("sum = " + sum);
//    }

}

package com.geek.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class Test {

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

    // 普通。
    private static void test1() {
        long start = System.currentTimeMillis();

        Long sum = 0L;
        for (Long i = 1L; i <= 1_000_000_000; i++) {
            sum += i;
        }
        System.out.println("sum = " + sum);
        long end = System.currentTimeMillis();
        System.out.println("时间 ~ " + (end - start));
        // sum = 500000000500000000
        //时间 ~ 6155
    }

    // ForkJoin。
    private static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 1_000_000_000L);
//        forkJoinPool.execute(task);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务。
        Long sum = submit.get();
        System.out.println("sum = " + sum);
        long end = System.currentTimeMillis();
        System.out.println("时间 ~ " + (end - start));
        // sum = 43715662942646859
        //时间 ~ 28120
    }

    // Stream 并行流。
    private static void test3() {
        long start = System.currentTimeMillis();

        // (]
        long sum = LongStream.rangeClosed(0L, 1_000_000_000L).parallel().reduce(0, Long::sum);
        System.out.println("sum = " + sum);
        long end = System.currentTimeMillis();
        System.out.println("时间 ~ " + (end - start));
        // sum = 500000000500000000
        //时间 ~ 293
    }

}



异步回调。

package com.geek.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 异步调用。
 * // 异步执行。
 * // 成功回调。
 * // 失败回调。
 */
public class Demo01 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 发起一个请求。
//        // 没有返回值的 runAsync(); 异步回调。
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName());
//        });
//
//        System.out.println(111);
//
//        // 阻塞获取执行结果。
//        completableFuture.get();

        // 有返回值的 supplyAsync(); 异步回调。

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + " ~ supplyAsync(); ~ Integer");
            int i = 1 / 0;
            return 1024;
        });

        // 成功的回调。
        CompletableFuture<Integer> completableFuture1 = completableFuture.whenComplete((t, u) -> {
            System.out.println("t = " + t);// 正常的返回结果。
            System.out.println("u = " + u);// 错误信息。
        }).exceptionally((e) -> {// 失败的回调。
            e.printStackTrace();
            System.out.println(e.getMessage());// java.lang.ArithmeticException: / by zero
            return 233;// 返回错误的结果。
        });
        System.out.println(completableFuture1.get());// 233
    }

}



JMM。

  • volatile 是 Java 虚拟机提供的轻量级的同步机制。
  • 保证可见性。
  • 不保证原子性。
  • 禁止指令重排。
  • JMM ~ Java Memory Model。
    Java 内存模型。不存在的东西,概念,约定。
  • 线程解锁前,必须把共享变量立刻刷回主存。
  • 线程加锁前,必须读取主存中的最新值到工作内存中。
  • 加锁和解锁是同一把锁。
8 种操作。

在这里插入图片描述

  • lock(锁定)。
    作用于主内存的变量,把一个变量标识为线程独占状态。
  • unlock(解锁)。
    作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取)。
    作用于主内存变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。
  • load(载入)。
    作用于工作内存的变量,把 read 操作的变量从主存中变量放入工作内存中。
  • use(使用)。
    作用于工作内存中的变量,把工作内存中的变暈传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令。
  • assign(赋值)。
    作用于工作内存中的变量,把一个从执行引擎中接受到的值放入工作内存的变量副本中。
  • store(存储)。
    作用于主内存中的变量,把 store 操作从工作内存中得到的变量的值放入主内存的变量中。
  • write(写入)。
    作用于主内存中的变量,把一个从工作内存中一个变量的值传送到主内存中,以便后续的 write 使用。
  • JMM 对这八种指令的使用,制定了如下规则。
  • 不允许 read 和 load、store 和 write 操作之一单独出现。即使用了 read 必须 load,使用了 store 必须 write。
  • 不允许线程丢弃他最近的 assign 操作,即工作变量的数据改变了之后必须告知主存。
  • 不允许一个线程将没有 assign 的数据从工作内存同步回主内存。
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对量实施 use、store 操作之前,必须经过 assign 和 load 操作。
  • 一个变量同一时间只有一个线程能对其进行 lock。多次 lock 后必须执行相同次数的 unlock 才能解锁。
  • 如果对一个变量进行 lock 操作,会清空所有工作内存中此变暈的值,在执行引擎使用这个变量前,必须重新 load 或 assign 操作初始化变量的值。
  • 如果一个变量没有被 lock,就不能对其进行 unlock 操作。也不能 unlock 一个被其他线程锁住的变量。
  • 对一个变量行 unlock 操作之前,必须把此变量同步回主内存。


volatile 可见性和非原子性验证。

volatile

a. 易变的;无定性的;无常性的;可能急剧波动的;不稳定的;易恶化的;易挥发的;易发散的

  • 可见性。
package com.geek;

import java.util.concurrent.TimeUnit;

public class JMMDemo02 {

    // 不加 volatile 程序就会死循环。
    // 加 volatile 可以保证可见性。
    volatile static int number = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (number == 0) {

            }
        }).start();

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

        number = 1;
        System.out.println(number);
    }

}

  • 不保证原子性。
    原子性:不可分割。

线程 A 在执行的时候,不能被打扰,也不能被分割。要么帕里执行成功,要么同时执行失败。

package com.geek;

/**
 * 不保证原子性。
 */
public class JMMDemo03 {

    // volatile 不能保证原子性。
    private volatile static int number = 0;

    private static void add() {
        ++number;// 不是一个原子操作。
    }

    public static void main(String[] args) {
        // 理论上 number 结果为 20000。
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        // main 和 gc 线程。只要大于 2,说明线程还没执行完。
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " ~ " + number);
        // main ~ 18307
        // main ~ 18526
        // synchronized 可保证 20000。但 volatile 不能保证。
        // Non-atomic operation on volatile field 'number' less... (Ctrl+F1)
        //Inspection info: Reports any non-atomic operations on volatile fields. Non-atomic operations on volatile fields are operations where the field is read and the value is used to update the field. It is possible for the value of the field to change between the read and the write, possibly invalidating the operation. The non-atomic operation can be avoided by surrounding it with a synchronized block or by making use of one of the classes from the java.util.concurrent.atomic package.
    }

}

  • 为什么 ++number; 不是原子性操作。
G:\lyfGeek\IdeaProjects\geek_juc\jvm\target\classes\com\geek>javap -c JMMDemo03.class
Compiled from "JMMDemo03.java"
public class com.geek.JMMDemo03 {
  public com.geek.JMMDemo03();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void add();
    Code:
       0: getstatic     #2                  // Field number:I
       3: iconst_1
       4: iadd
       5: putstatic     #2                  // Field number:I
       8: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: bipush        20
       5: if_icmpge     29
       8: new           #3                  // class java/lang/Thread
      11: dup
      12: invokedynamic #4,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
      17: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      20: invokevirtual #6                  // Method java/lang/Thread.start:()V
      23: iinc          1, 1
      26: goto          2
      29: invokestatic  #7                  // Method java/lang/Thread.activeCount:()I
      32: iconst_2
      33: if_icmple     42
      36: invokestatic  #8                  // Method java/lang/Thread.yield:()V
      39: goto          29
      42: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      45: new           #10                 // class java/lang/StringBuilder
      48: dup
      49: invokespecial #11                 // Method java/lang/StringBuilder."<init>":()V
      52: invokestatic  #12                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      55: invokevirtual #13                 // Method java/lang/Thread.getName:()Ljava/lang/String;
      58: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
      61: ldc           #15                 // String  ~
      63: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
      66: getstatic     #2                  // Field number:I
      69: invokevirtual #16                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

      72: invokevirtual #17                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      75: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      78: return

  static {};
    Code:
       0: iconst_0
       1: putstatic     #2                  // Field number:I
       4: return
}

  • 如果不加 lock 和 synchronized,怎么保证原子性。

使用原子类解决原子性问题。

java.util.concurrent.atomic。

package com.geek;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 不保证原子性。
 */
public class JMMDemo03 {

    // volatile 不能保证原子性。
//    private volatile static int number = 0;
    private volatile static AtomicInteger number = new AtomicInteger();

    public static void add() {
//        ++number;// 不是一个原子操作。
        number.getAndIncrement();// CAS。
    }

    public static void main(String[] args) {
        // 理论上 number 结果为 20000。
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        // main 和 gc 线程。只要大于 2,说明线程还没执行完。
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " ~ " + number);
        // main ~ 20000
}



指令重排。

你写的程序,计算机并不是按照你写的那样执行的。

源代码 -> 编译器优化重排 -> 指令并行也可能发生重排 ->
内存系统也会重排 --> 执行。

处理器在进行指令重排时,会考虑数据之间的依赖性。

int x = 1;// 1
int y = 2;// 2
x = x + 5;// 3
y = y + x;// 4

期望 1 2 3 4,但执行时可能 2 1 3 41 3 2 4.
但不可能 4 1 2 3

volatile 可以避免指令重排。

内存屏障。

保证特定操作的执行顺序。
保证某些变量的内存可见性。

在这里插入图片描述



volatile 可以保持可见性,不能保证原子性。由于内存屏障,可以保证避免指令重排现象。



单例模式。

package com.geek.single;

/**
 * 饿汉式单例。
 */
public class Hungry {

    // 如果一个饿汉单例类中有如下数据,这个类一加载就会加载这些数据,会浪费空间。
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];

    private final static Hungry HUNGRY = new Hungry();

    private Hungry() {

    }

    public static Hungry getInstance() {
        return HUNGRY;
    }

}

package com.geek.single;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lazy instance = Lazy.getInstance();
                System.out.println("instance = " + instance);
            }).start();
        }
    }

}


/*
Thread-0
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
instance = com.geek.single.Lazy@37309d4b
 */
package com.geek.single;

/**
 * 静态内部类。
 */
public class Holder {

    private Holder() {

    }

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

    private static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }

}



反射破坏单例。
package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                Lazy instance = Lazy.getInstance();
//                System.out.println("instance = " + instance);
//            }).start();
//        }

        // 反射破坏单例。
        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();
        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
        // instance1 = com.geek.single.Lazy@74a14482
        //instance = com.geek.single.Lazy@1540e19d
    }

}

  • 道高一尺魔高一丈。阻止反射破坏单例。
package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());

        // 道高一尺魔高一丈。阻止反射破坏单例。
        synchronized (Lazy.class) {
            if (null != lazy) {
                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
            }
        }
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                Lazy instance = Lazy.getInstance();
//                System.out.println("instance = " + instance);
//            }).start();
//        }

        // 反射破坏单例。
        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();
        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
        // instance1 = com.geek.single.Lazy@74a14482
        //instance = com.geek.single.Lazy@1540e19d
    }

}

  • 上面是一个对象正常获取,一个对象通过反射。
    但二个都通过反射,还是存在问题。
package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());

        // 道高一尺魔高一丈。阻止反射破坏单例。
        synchronized (Lazy.class) {
            if (null != lazy) {
                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
            }
        }
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                Lazy instance = Lazy.getInstance();
//                System.out.println("instance = " + instance);
//            }).start();
//        }

        // 反射破坏单例。
//        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();
        Lazy instance = declaredConstructor.newInstance();
        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
        // instance1 = com.geek.single.Lazy@74a14482
        //instance = com.geek.single.Lazy@1540e19d
    }

}

  • 解决。通过设置一个外部不知道的变量名(不知道变量名,无法通过反射获取),判断这个变量是否有改动。
package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    // 谁都不知道的变量名。
    private static boolean geek = false;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());

        // 道高一尺魔高一丈。阻止反射破坏单例。
        synchronized (Lazy.class) {

            if (!geek) {
                geek = true;
            } else {
                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
            }

//            if (null != lazy) {
//                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
//            }
        }
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                Lazy instance = Lazy.getInstance();
//                System.out.println("instance = " + instance);
//            }).start();
//        }

        // 反射破坏单例。
//        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();
        Lazy instance = declaredConstructor.newInstance();
        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
        // instance1 = com.geek.single.Lazy@74a14482
        //instance = com.geek.single.Lazy@1540e19d
    }

}

  • 没完了。如果这个变量名也被知道了。又被破坏了。
package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例。
 *
 * @author geek
 */
public class Lazy {

    private volatile static Lazy lazy;

    // 谁都不知道的变量名。
    private static boolean geek = false;

    private Lazy() {
        System.out.println(Thread.currentThread().getName());

        // 道高一尺魔高一丈。阻止反射破坏单例。
        synchronized (Lazy.class) {

            if (!geek) {
                geek = true;
            } else {
                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
            }

//            if (null != lazy) {
//                throw new RuntimeException("异常:不要试图使用反射破坏单例。。");
//            }
        }
    }

    /**
     * 双重检测锁 懒汉式单例。
     *
     * @return
     */
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();// 不是一个原子性操作。
                    /**
                     * 1. 分配内存空间。
                     * 2. 执行构造方法,初始化对象。
                     * 3. 把引用指向对象。
                     *
                     * 期望 123
                     * 然而可能 132
                     *      A 线程先分配内存空间,引用指向了对象,
                     *      这时 B 线程进来,会认为这个引用不为 null
                     *      直接 return lazy;。
                     *      此时 lazy 还没有完成构造,return null。
                     * (指令重排造成问题。)
                     *
                     * 所以要在 lazy 加上 volatile。
                     */
                }
            }
        }
        return lazy;
    }

    /**
     * 多线程。
     *
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                Lazy instance = Lazy.getInstance();
//                System.out.println("instance = " + instance);
//            }).start();
//        }

        // 反射破坏单例。

        // 再次破坏 boolean。
        Class<Lazy> lazyClass = Lazy.class;
        Field geek = lazyClass.getDeclaredField("geek");
        geek.setAccessible(true);


//        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Lazy instance1 = declaredConstructor.newInstance();

        geek.set(instance1, false);

        Lazy instance = declaredConstructor.newInstance();

        System.out.println("instance1 = " + instance1);
        System.out.println("instance = " + instance);
//instance1 = com.geek.single.Lazy@1540e19d
//instance = com.geek.single.Lazy@677327b6
    }

}

  • newInstance(); ~ 源码得知,枚举类型不能通过反射创建。
    /**
     * Uses the constructor represented by this {@code Constructor} object to
     * create and initialize a new instance of the constructor's
     * declaring class, with the specified initialization parameters.
     * Individual parameters are automatically unwrapped to match
     * primitive formal parameters, and both primitive and reference
     * parameters are subject to method invocation conversions as necessary.
     *
     * <p>If the number of formal parameters required by the underlying constructor
     * is 0, the supplied {@code initargs} array may be of length 0 or null.
     *
     * <p>If the constructor's declaring class is an inner class in a
     * non-static context, the first argument to the constructor needs
     * to be the enclosing instance; see section 15.9.3 of
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * <p>If the required access and argument checks succeed and the
     * instantiation will proceed, the constructor's declaring class
     * is initialized if it has not already been initialized.
     *
     * <p>If the constructor completes normally, returns the newly
     * created and initialized instance.
     *
     * @param initargs array of objects to be passed as arguments to
     * the constructor call; values of primitive types are wrapped in
     * a wrapper object of the appropriate type (e.g. a {@code float}
     * in a {@link java.lang.Float Float})
     *
     * @return a new object created by calling the constructor
     * this object represents
     *
     * @exception IllegalAccessException    if this {@code Constructor} object
     *              is enforcing Java language access control and the underlying
     *              constructor is inaccessible.
     * @exception IllegalArgumentException  if the number of actual
     *              and formal parameters differ; if an unwrapping
     *              conversion for primitive arguments fails; or if,
     *              after possible unwrapping, a parameter value
     *              cannot be converted to the corresponding formal
     *              parameter type by a method invocation conversion; if
     *              this constructor pertains to an enum type.
     * @exception InstantiationException    if the class that declares the
     *              underlying constructor represents an abstract class.
     * @exception InvocationTargetException if the underlying constructor
     *              throws an exception.
     * @exception ExceptionInInitializerError if the initialization provoked
     *              by this method fails.
     */
    @CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

↓ ↓ ↓


idea 的 class 文件中显示有空参构造。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.geek.single;

public enum EnumSingle {
    INSTANCE;

    private EnumSingle() {
    }

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

于是用反射空参对象,报错 ~ 没有空参构造器。IDEA 个骗子。

package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 枚举本身也是一个 Class 类。
 */
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }

}


class Test {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;

        System.out.println("instance1 = " + instance1);
        System.out.println("instance2 = " + instance2);
        // instance1 = INSTANCE
        //instance2 = INSTANCE

        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        // Exception in thread "main" java.lang.NoSuchMethodException: com.geek.single.EnumSingle.<init>()
        EnumSingle enumSingle1 = declaredConstructor.newInstance();
        EnumSingle enumSingle2 = declaredConstructor.newInstance();
        System.out.println("enumSingle1 = " + enumSingle1);
        System.out.println("enumSingle2 = " + enumSingle2);
    }

}

反编译看看,但是还是有空参构造。骗子!

> javap -p EnumSingle.class
Compiled from "EnumSingle.java"
public final class com.geek.single.EnumSingle extends java.lang.Enum<com.geek.single.EnumSingle> {
  public static final com.geek.single.EnumSingle INSTANCE;
  private static final com.geek.single.EnumSingle[] $VALUES;
  public static com.geek.single.EnumSingle[] values();
  public static com.geek.single.EnumSingle valueOf(java.lang.String);
  private com.geek.single.EnumSingle();
  public com.geek.single.EnumSingle getInstance();
  static {};
}

  • 使用 jad 反编译。

工具下载:https://download.csdn.net/download/lyfGeek/12673711

jad.exe -sjava EnumSingle.class
Parsing EnumSingle.class… Generating EnumSingle.java

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.geek.single;


public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/geek/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

其实是有参构造器。

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

反射走起。

package com.geek.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 枚举本身也是一个 Class 类。
 */
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }

}


class Test {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;

        System.out.println("instance1 = " + instance1);
        System.out.println("instance2 = " + instance2);
        // instance1 = INSTANCE
        //instance2 = INSTANCE

        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        // Exception in thread "main" java.lang.NoSuchMethodException: com.geek.single.EnumSingle.<init>()
        EnumSingle enumSingle1 = declaredConstructor.newInstance();
        EnumSingle enumSingle2 = declaredConstructor.newInstance();
        System.out.println("enumSingle1 = " + enumSingle1);
        System.out.println("enumSingle2 = " + enumSingle2);
    }

    // instance1 = INSTANCE
    //instance2 = INSTANCE
    //Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    //	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    //	at com.geek.single.Test.main(EnumSingle.java:34)

}



深入理解 CAS。

package com.geek.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * CAS ~ Compare and Swap。
 * 比较并交换。
 */
public class CASDemo {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //     public final boolean compareAndSet(int expect, int update) {
        //      * Atomically sets the value to the given updated value
        //     * if the current value {@code ==} the expected value.
        // 如果现在的值 == 期望值,设置为新的值。
        boolean b = atomicInteger.compareAndSet(2020, 2021);
        System.out.println("b = " + b);
        System.out.println("atomicInteger = " + atomicInteger);

        boolean b1 = atomicInteger.compareAndSet(2020, 2021);
        System.out.println("b1 = " + b1);
        System.out.println("atomicInteger = " + atomicInteger);
        // b = true
        //atomicInteger = 2021
        //b1 = false
        //atomicInteger = 2021

        // CAS 是 CPU 的并发源语。
    }

}

在这里插入图片描述

  • Unsafe 类中都是 native 方法。

在这里插入图片描述

  • 先获取 var1 对象的内存地址偏移量 var2 的值,赋值给 var5。
  • 再“比较并交换” compareAndSwapInt();。
    如果 var1 对象的内存地址偏移量 var2 的值还是 var5,就让这个值(var5)+1(+ var4,var4 是参数传递进来的 1)。

内存操作。效率很高。

这是一个很标准的自旋锁体现。

CAS ~ 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作。如果不是就一直循环。

缺点。

  • 循环会耗时间。
  • 一次性只能保证一个共享变量的原子性。
  • ABA 问题。


CAS --> ABA 问题(狸猫换太子)。

在这里插入图片描述

package com.geek.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * CAS ~ Compare and Swap。
 * 比较并交换。
 */
public class ABADemo {

    // 对于 SQL ~ 乐观锁。

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(1);
        // ~ ~ ~ ~ ~ ~ ~ 捣乱的线程。 ~ ~ ~ ~ ~ ~ ~
        boolean b = atomicInteger.compareAndSet(1, 3);
        System.out.println("b = " + b);
        System.out.println("atomicInteger = " + atomicInteger);
        boolean b1 = atomicInteger.compareAndSet(3, 1);
        System.out.println("b1 = " + b1);
        System.out.println("atomicInteger = " + atomicInteger);

        // ~ ~ ~ ~ ~ ~ ~ 期望的线程。 ~ ~ ~ ~ ~ ~ ~
        boolean bb = atomicInteger.compareAndSet(1, 666);
        System.out.println("bb = " + bb);
        System.out.println("atomicInteger = " + atomicInteger);
    }

}



解决 ABA 问题 ~ 原子引用。

java.util.concurrent.atomic.AtomicReference

package com.geek.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * CAS ~ Compare and Swap。
 * 比较并交换。
 */
public class ABADemo {

    // 对于 SQL ~ 乐观锁。

    public static void main(String[] args) {
//        AtomicInteger atomicInteger = new AtomicInteger(1);

        //  【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
        //说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
//        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(2020, 1);
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();// 获得版本号。~ 1
            System.out.println("a 1 ~ stamp = " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 乐观锁,版本号 +1。
            System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));// true

            System.out.println("a 2 ~ stamp = " + atomicStampedReference.getStamp());// 2

            System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));// true

            System.out.println("a 3 ~ stamp = " + atomicStampedReference.getStamp());// 3

        }, "A").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();// 获得版本号。~ 1
            System.out.println("b 1 ~ stamp = " + stamp);// 1
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 乐观锁,版本号 +1。
            System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));// false
            System.out.println("b 1 ~ atomicStampedReference.getStamp() = " + atomicStampedReference.getStamp());// 3
        }, "B").start();
    }

}



锁。

公平锁、非公平锁。

公平锁(先来后到) 。

非公平锁(可以插队,默认)。

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }


可重入锁。

(递归锁)。

package com.geek.lock;

public class Demo01 {

    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(phone::sms, "A").start();

        new Thread(() -> phone.sms(), "B").start();
    }

}


class Phone {

    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName() + " ~ sms");
        call();
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + " ~ call");
    }

}

package com.geek.lock;

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

public class Demo02 {

    public static void main(String[] args) {
        Phone2 phone = new Phone2();

        new Thread(phone::sms, "A").start();

        new Thread(() -> phone.sms(), "B").start();
    }

}


class Phone2 {

    Lock lock = new ReentrantLock();

    public synchronized void sms() {
        lock.lock();
        // lock 锁必须配对,否则就会死在里面。
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " ~ sms");
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public synchronized void call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " ~ call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}



自旋锁。

在这里插入图片描述

package com.geek.lock;

import java.util.concurrent.TimeUnit;

public class TestSpinLock {

    public static void main(String[] args) {
//        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
//        reentrantLock.unlock();

        // 自己实现。自旋锁 CAS。
        SpinLockDemo lock = new SpinLockDemo();

        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T1").start();

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

        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T2").start();
    }

}

/*
T1 ~~~ mylock
T2 ~~~ mylock
T1 ~~~ myUnlock  // T1 自旋解锁后,T2 才能解锁。
T2 ~~~ myUnlock
 */



死锁。

在这里插入图片描述

package com.geek.lock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {

    public static void main(String[] args) {

        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA, lockB), "T1").start();
        new Thread(new MyThread(lockB, lockA), "T2").start();
    }

}


class MyThread implements Runnable {

    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + " lock -> " + lockA + ", get -> " + lockB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + " lock -> " + lockB + ", get -> " + lockA);
            }
        }
    }

}

/*
T1 lock -> lockA, get -> lockB
T2 lock -> lockB, get -> lockA

Process finished with exit code -1
 */
  • 解决。

\jdk1.8.0_241\bin\ 下的 jps.exe。

jps -l

G:\lyfGeek\ProgramFiles\Java\jdk1.8.0_241\bin>jps -l
16416 org.jetbrains.jps.cmdline.Launcher
17664
18624 com.geek.lock.DeadLockDemo
19508 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
18044 sun.tools.jps.Jps
20396 org.jetbrains.idea.maven.server.RemoteMavenServer

使用 jstack 进程号

G:\lyfGeek\ProgramFiles\Java\jdk1.8.0_241\bin>jstack 18624
2020-11-08 12:29:54
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.241-b07 mixed mode):

"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000002dc3000 nid=0x4d98 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"T2" #13 prio=5 os_prio=0 tid=0x000000001e7d1000 nid=0xa08 waiting for monitor entry [0x000000001f70f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.geek.lock.MyThread.run(DeadLockDemo.java:41)
        - waiting to lock <0x000000076bb9bf28> (a java.lang.String)
        - locked <0x000000076bb9bf60> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

"T1" #12 prio=5 os_prio=0 tid=0x000000001e7ce800 nid=0x4664 waiting for monitor entry [0x000000001f60e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.geek.lock.MyThread.run(DeadLockDemo.java:41)
        - waiting to lock <0x000000076bb9bf60> (a java.lang.String)
        - locked <0x000000076bb9bf28> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001e79e000 nid=0x47c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001e700800 nid=0x1b20 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001e6fd800 nid=0xb9c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001e6f8000 nid=0x4fc8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001e6f3800 nid=0x4d40 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001e6e6000 nid=0x4d64 runnable [0x000000001ef0e000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x000000076ba84df8> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x000000076ba84df8> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e649000 nid=0x4564 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e6a2800 nid=0x1e1c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001e631800 nid=0x2990 in Object.wait() [0x000000001ec0f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b908ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x000000076b908ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002eb9000 nid=0x4a5c in Object.wait() [0x000000001eb0f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b906c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000076b906c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000001c847800 nid=0x3200 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002dd8800 nid=0x2cd4 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002dda000 nid=0xef8 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002ddc000 nid=0x4cc4 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002ddd800 nid=0x31bc runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002ddf800 nid=0x3c20 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002de1000 nid=0x3524 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002de5000 nid=0x367c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002de6000 nid=0x4660 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001e7cd000 nid=0x2c40 waiting on condition

JNI global references: 12


Found one Java-level deadlock:
=============================
"T2":
  waiting to lock monitor 0x000000001c850e38 (object 0x000000076bb9bf28, a java.lang.String),
  which is held by "T1"
"T1":
  waiting to lock monitor 0x000000001c853358 (object 0x000000076bb9bf60, a java.lang.String),
  which is held by "T2"

Java stack information for the threads listed above:
===================================================
"T2":
        at com.geek.lock.MyThread.run(DeadLockDemo.java:41)
        - waiting to lock <0x000000076bb9bf28> (a java.lang.String)
        - locked <0x000000076bb9bf60> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)
"T1":
        at com.geek.lock.MyThread.run(DeadLockDemo.java:41)
        - waiting to lock <0x000000076bb9bf60> (a java.lang.String)
        - locked <0x000000076bb9bf28> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.




工作中排查问题。

  • 日志。

  • 堆栈。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lyfGeek

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

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

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

打赏作者

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

抵扣说明:

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

余额充值