JUC并发编程

JUC并发编程

Java.Util .Concurrent

什么是JUC

  Runavle 没有返回值、效率相比Callabe相对较低!

线程和进程

一句话包括

  进程:一个程序,.exe程序的集合。
  一个进程往往包含多个线程,至少包含一个!
  Java默认进程有两个:mian、GC。
  线程:开了一个进程Typora,写字,自动保存(线程负责的)
  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 */
            }
        }
    }
	/*本地方法,底层的C++,Java无法直接操作硬件*/
    private native void start0();

并发、并行

  并发编程:并发:多线程操作同一个资源(CPU一核,模拟出来多条线程);并行:多个人一起行动(CPU多核,多个线程可以同时执行)。

package com.ly;

public class Test1 {
    public static void main(String[] args) {
        System.out.println("----------------------开始----------------------");
        /*new Thread().start();*/
        /*获取cpu的核数*/
        /*CPU密集型,IO密集型*/
        System.out.println(Runtime.getRuntime().availableProcessors());
        System.out.println("----------------------结束----------------------");
    }
}

----------------------开始----------------------
4
----------------------结束----------------------

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

线程有几个状态

public enum State {
        /*新生*/
        NEW,

        /*运行*/
        RUNNABLE,

        /*阻塞*/
        BLOCKED,

        /*等待*/
        WAITING,

        /*超时等待*/
        TIMED_WAITING,

        /*终止*/
        TERMINATED;
    }

wait/sleep区别

  1、来自不同的类:wait=>Object;sleep=>Thread。
  2、关于锁的释放:wait会释放锁,sleeo睡觉了,抱着锁睡觉 ,不会释放。
  3、使用的范围是不同的: wait:必须在同步代码块中;
sleep:可以在任意地方睡。
  4、是否需要捕获异常: wait不需要捕获异常;sleep必须要捕获异常。

Lock锁

传统Synchronized

  公平锁:十分公平,可以先来后到;非公平锁:非常不公平:可以插队(默认)。

package com.ly;

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

/*基本的买票例子*/
/*线程就是一个单独的资源类,没有任何附属的操作*/
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        System.out.println("----------------------开始----------------------");

        /*并发:多线程操作同一个资源类,把资源包丢入线程*/
        Ticket ticket = new Ticket();
        /*@FunctionalInterface 函数是接口
         * lambda表带达式(参数)->{代码}
         */
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "C").start();
        System.out.println("----------------------结束----------------------");
    }
}

//Lock
/*1、new ReentrantLock();
2、lock.lock();加锁
3、finally=>lock.unlock();解锁*/
class Ticket {
    Lock lock = new ReentrantLock();
    /*属性、方法*/
    private int num = 30;

    public void sale() {
        lock.lock();//加锁

        try {
            /*业务代码*/
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + (num--) + "票,剩余" + num);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();/*解锁*/
        }
    }
}
A卖出了30票,剩余29
C卖出了29票,剩余28
C卖出了28票,剩余27
C卖出了27票,剩余26
C卖出了26票,剩余25
C卖出了25票,剩余24
C卖出了24票,剩余23
C卖出了23票,剩余22
C卖出了22票,剩余21
C卖出了21票,剩余20
C卖出了20票,剩余19
C卖出了19票,剩余18
C卖出了18票,剩余17
C卖出了17票,剩余16
C卖出了16票,剩余15
C卖出了15票,剩余14
C卖出了14票,剩余13
C卖出了13票,剩余12
C卖出了12票,剩余11
C卖出了11票,剩余10
C卖出了10票,剩余9
C卖出了9票,剩余8
C卖出了8票,剩余7
C卖出了7票,剩余6
C卖出了6票,剩余5
C卖出了5票,剩余4
C卖出了4票,剩余3
C卖出了3票,剩余2
C卖出了2票,剩余1
C卖出了1票,剩余0

Synchronized 和 Lock区别

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

锁是什么?如何判断锁的是谁!

生产者和消费者的问题

生产者和消费者的问题(Synchronized)

package pc;

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

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

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

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

A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0

问题存在,A B C D 4个线程安不安全

  将该问题扩展化,如果有同时ABCD四个进程。

package pc;

/**
 * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替通信 A B 操作同一个变量 num=0
 * A num+1
 * B num+1
 */
public class A {
    public static void main(String[] args) {

        Date date = new Date();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

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

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

    /*-1*/
    public synchronized void decrement() throws InterruptedException {

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

A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
C=>1
A=>2
B=>1
C=>2
D=>1
D=>0
A=>1
D=>0
C=>1
D=>0
A=>1
D=>0
C=>1
D=>0
A=>1
D=>0
C=>1
D=>0
A=>1
B=>0
D=>-1
D=>-2
C=>-1
B=>-2
B=>-3
B=>-4
C=>-3

  从官方文档可知,在没有被通知、中断或超时的情况下,线程还可以唤醒一个所谓的虚假唤醒 (spurious wakeup)。
  虽然这种情况在实践中很少发生,但是应用程序必须通过以下方式防止其发生,即对应该导致该线程被提醒的条件进行测试,如果不满足该条件,则继续等待。
  换句话说,等待应总是发生在循环中,如下面的示例:

    synchronized (obj)
{
        while (<condition does not hold>)
        obj.wait(timeout);
... // Perform action appropriate to condition
    }

  解决该问题的方法为:将if判断改为while判断

package pc;

/**
 * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替通信 A B 操作同一个变量 num=0
 * A num+1
 * B num+1
 */
public class A {
    public static void main(String[] args) {

        Date date = new Date();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

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

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

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

A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0
C=>1
D=>0

  if 判断一次,处于C进程则不会停;while 判断,一但A被修改,则C会停下来等待,使用while可以防止虚假唤醒。

生产者和消费者的问题(JUC)

package pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*@SuppressWarnings("all")*/

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

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

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

    /**
     * @throws InterruptedException
     */
    /*-1*/
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            /*业务代码*/
            /*将if判断改为while判断*/
            while (number == 0) {
                /*等待*/
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            /*通知其他线程,我-1完毕了*/
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

A=>1
B=>0
C=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
B=>0
C=>1
D=>0
A=>1
D=>0

进程已结束,退出代码0

  public interface Condition
  Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。
  其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
  条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。
  因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。
  等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition

  任何一个新的技术,绝对不仅仅知识覆盖了原来的技术,它一定会有优势和补充。
  根据目前的调试结构来看,产生的结果随机分布的状态,我们无法去控制,那么,如何去优化这个算法,是程序有序执行。

package 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 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

/**
 * 资源类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;/*假设1A 2B 3C*/

    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();
        }
    }
}

"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2020.3\lib\idea_rt.jar=52074:D:\IntelliJ IDEA 2020.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;D:\Project\JUC\target\classes;C:\Users\梦\.m2\repository\org\projectlombok\lombok\1.18.4\lombok-1.18.4.jar" pc.C
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC
A=> AAAAA
B=> BBBBB
C=> CCCCC

进程已结束,退出代码0

8锁现象

1、标准情况下,两个线程,先发短信还是打电话?1 发短信  2 打电话

package lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        /*锁的存在*/
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        /*捕获*/
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "A").start();
    }
}

class Phone {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    public synchronized void sendSms() {
        System.out.println("发短信");
    }

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

发短信
打电话

进程已结束,退出代码0

2、sendSms延迟4秒,标准情况下,两个线程,先发短信还是打电话?1发短信 2打电话

package lock8;
import java.util.concurrent.TimeUnit;
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        /*锁的存在*/
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        /*捕获*/
        new Thread(() -> {
            phone.call();
        }, "A").start();
    }
}

class Phone {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

发短信
打电话

进程已结束,退出代码0

3、增加了一个普通方法!先执行发短信还是hello 普通方法

package lock8;
import java.util.concurrent.TimeUnit;
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone2 = new Phone2();
        /*锁的存在*/
        new Thread(() -> {
            phone2.sendSms();
        }, "A").start();
        /*捕获*/
        new Thread(() -> {
            phone2.hello();
        }, "A").start();
    }
}
class Phone2 {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
     /*这里没有锁,不是同步方法*/
    public void hello(){
        System.out.println("hello ");
    }
}

hello 
发短信

进程已结束,退出代码0

4、两个对象,两个同步方法,先发短信还是打电话?1发短信 2打电话

package lock8;

import java.util.concurrent.TimeUnit;


public class Test4 {
    public static void main(String[] args) {
        /*两个对象*/
        Phone4 phone41 = new Phone4();
        Phone4 phone42 = new Phone4();
        /*锁的存在*/
        new Thread(() -> {
            phone41.sendSms();
        }, "A").start();
        /*捕获*/
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone42.call();
        }, "A").start();
    }
}

class Phone4 {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

    public void hello() {
        System.out.println("hello ");
    }
}

打电话
发短信

进程已结束,退出代码0

5、增加两个静态的同步方法

package lock8;

import java.util.concurrent.TimeUnit;


public class Test5 {
    public static void main(String[] args) {
        /*两个对象*/
        Phone5 phone5 = new Phone5();
        /*锁的存在*/

        new Thread(() -> {
            Phone5.sendSms();
        }, "A").start();
        /*捕获*/
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone5.call();
        }, "A").start();
    }
}

/*Phone5唯一的一个class对象*/
class Phone5 {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    /*static 静态方法
    类一加载就有了!锁的是Class模板*/
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

}

发短信
打电话

进程已结束,退出代码0

## 一个静态的同步方法,一个普通的同步方法,先打印发短信还是打电话?

package lock8;

import java.util.concurrent.TimeUnit;

public class Test6 {
    public static void main(String[] args) {
        /*两个对象*/
        Phone6 phone6= new Phone6();
        /*锁的存在*/

        new Thread(() -> {
            Phone6.sendSms();
        }, "A").start();
        /*捕获*/
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone6.call();
        }, "B").start();
    }
}

/*Phone5唯一的一个class对象*/
class Phone6 {
    /*synchronized 锁的对象是方法的调用者*/
    /*两个方法是同一个锁吗,谁先拿到谁执行*/
    /*static 静态方法
    类一加载就有了!锁的是Class模板*/
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

打电话
发短信

进程已结束,退出代码0

小结

  new this 具体的一个手机
  static Class具体的一个模板

集合类不安全**

List不安全

package unsafe;

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

public class ListTset {
    public static void main(String[] args) {
   	    System.out.println("输出结果:");
        System.out.println();
        /*并发下ArrayList 不安全的*/
        /**
         * 解决方法:
         * 1、List<String> list = new Vector<>();不提倡
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3、List<String> list = new CopyOnWriteArrayList<>();
         */
       /* CopyOnWrite写入时复制,COW计算机程序设计领域的一种优化策略;
        多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        在写入的时候避免覆盖,造成数据问题;*/
        /*读写分离的思想 */
        /*CopyOnWriteArrayList比Vector牛逼在哪里?只要有synchronized方法它的效率相对比较低,写入时复制读写分离*/
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:

[c5ad9]
[c5ad9, dc90d, 4358f]
[c5ad9, dc90d]
[c5ad9, dc90d, 4358f, ffe58]
[c5ad9, dc90d, 4358f, ffe58, 5770d]
[c5ad9, dc90d, 4358f, ffe58, 5770d, b526d, a66c9]
[c5ad9, dc90d, 4358f, ffe58, 5770d, b526d]
[c5ad9, dc90d, 4358f, ffe58, 5770d, b526d, a66c9, 5c310]
[c5ad9, dc90d, 4358f, ffe58, 5770d, b526d, a66c9, 5c310, 251b5, b5a5a]
[c5ad9, dc90d, 4358f, ffe58, 5770d, b526d, a66c9, 5c310, 251b5]

进程已结束,退出代码0

Set不安全

package unsafe;

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

/*同理可证 :ConcurrentModificationException*/
public class SetTest {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        /*Set<String> set = new HashSet<>();*/
       /* Set<String> set = 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);
            }, String.valueOf(i)).start();
        }
    }
}

已连接到目标 VM, 地址: ''127.0.0.1:53694',传输: '套接字''
输出结果:

[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a, c54b1]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a, c54b1, 9a6ea]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a, c54b1, 9a6ea, dea84]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a, c54b1, 9a6ea, dea84, 55157]
[5169f, a44da, f89d4, e9ad6, 01353, 39ef5, debfb, 1b49e, 4b0b3, f2b02, 3c401, 736a6, 094f2, f38c8, bb0bd, a0273, 30871, 348d5, 74ba1, f2357, 805d5, 3c439, ef786, d135a, 0d98a, c54b1, 9a6ea, dea84, 55157, 63113]
与目标 VM 断开连接, 地址为: ''127.0.0.1:53694',传输: '套接字''

进程已结束,退出代码0

HashSet的底层是什么?
  HashSet的底层就相当于new了一个HashMap

 public HashSet() {
        map = new HashMap<>();
    }
 /*add set 本质就是 map key 是无法重复的!*/
. public boolean add(E e){
 		return map.put(e,PRESNT == null);/*不变的值*/
    }

HashMap 不安全

package unsafe;

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

/**
 * ConcurrentModificationException 并发修改日常
 */
public class MapTest {
    public static void main(String[] args) {
        /**
         * map是这样用的吗?
         * 默认等价于什么? 加载因子、初始容量 new HashMap<>(16,0.75);
         * HashMap<String, String> map = new HashMap<>();
         */
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 31; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:

{11=502e3, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 2=8f176, 13=3056d, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 2=8f176, 14=81b74, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 2=8f176, 14=81b74, 3=a992d, 15=b4881, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 2=8f176, 14=81b74, 3=a992d, 15=b4881, 4=a660f, 16=74726, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1}
{11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1, 21=d6366}
{11=502e3, 22=72325, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 28=feeb6, 29=0d040, 30=c90b3, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 0=bef16, 1=835a1, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 28=feeb6, 29=0d040, 30=c90b3, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 0=bef16, 1=835a1, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 28=feeb6, 29=0d040, 30=c90b3, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 28=feeb6, 29=0d040, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 28=feeb6, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 27=ca5b8, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 26=a2265, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{22=72325, 23=81fd8, 24=27b45, 25=41bd5, 10=4fff1, 11=502e3, 12=56d55, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 21=d6366}
{11=502e3, 22=72325, 12=56d55, 23=81fd8, 13=3056d, 24=27b45, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1, 21=d6366}
{11=502e3, 22=72325, 12=56d55, 23=81fd8, 13=3056d, 14=81b74, 15=b4881, 16=74726, 17=6f8b2, 18=4aeca, 19=678db, 2=8f176, 3=a992d, 4=a660f, 5=02c42, 6=16cc0, 7=94daf, 8=f9ec8, 9=926d2, 20=49b54, 10=4fff1, 21=d6366}

进程已结束,退出代码0

## 什么是ConcurrentHashMap?ConcurrentHashMap的原理
  以下内容来自官方文档

public class ConcurrentHashMap<K,V>extends AbstractMap<K,V>implements ConcurrentMap<K,V>, Serializable

  支持获取的完全并发和更新的所期望可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。
  不过,尽管所有操作都是线程安全的,但获取操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。
  此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。
  获取操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put 和 remove)。
  获取会影响最近完成的 更新操作的结果。
  对于一些聚合操作,比如 putAll 和 clear,并发获取可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators 和 Enumerations 返回在某一时间点上影响哈希表状态的元素。
  它们不会 抛出 ConcurrentModificationException。
  不过,迭代器被设计成每次仅由一个线程使用。
  这允许通过可选的 concurrencyLevel 构造方法参数(默认值为 16)来引导更新操作之间的并发,该参数用作内部调整大小的一个提示。
  表是在内部进行分区的,试图允许指示无争用并发更新的数量。
  因为哈希表中的位置基本上是随意的,所以实际的并发将各不相同。
  理想情况下,应该选择一个尽可能多地容纳并发修改该表的线程的值。
  使用一个比所需要的值高很多的值可能会浪费空间和时间,而使用一个显然低很多的值可能导致线程争用。
  对数量级估计过高或估计过低通常都会带来非常显著的影响。
  当仅有一个线程将执行修改操作,而其他所有线程都只是执行读取操作时,才认为某个值是合适的。
  此外,重新调整此类或其他任何种类哈希表的大小都是一个相对较慢的操作,因此,在可能的时候,提供构造方法中期望表大小的估计值是一个好主意。
  此类及其视图和迭代器实现了 Map 和 Iterator 接口的所有可选 方法。
  此类与 Hashtable 相似,但与 HashMap 不同,它不 允许将 null 用作键或值。
  此类是 Java Collections Framework 的成员。

Callable

  Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
  但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
  可以有返回值;可以抛出异常;方法不同,run()、call()。
  java.lang
  接口 Runnable
  所有已知子接口:
    RunnableFuture, RunnableScheduledFuture
  所有已知实现类:
    AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, SwingWorker, Thread, TimerTask

package callable;

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

@SuppressWarnings("all")
public class CallableTset {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("输出结果:");
        System.out.println();
        /**
         * new Thread(new MyThread()).start();
         * new Thread(new FutureTask<v>()).start();
         * new Thread(new FutureTask<v>(Callable)).start();
         */
        new Thread().start();/*怎么启动Callable*/
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);
        /*适配类*/
        new Thread(futureTask, "A").start();
        Integer o = (Integer) futureTask.get();/*获取Callable的返回结果*/
        System.out.println(o);
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() {
        System.out.println("call()");
        return 1024;
    }
}

输出结果:

call()
1024

进程已结束,退出代码0

常用的辅助类

CountDownLatch

public class CountDownLatchextends Object

  一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  用给定的计数 初始化 CountDownLatch。
  由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。
  之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。
  这种现象只出现一次——计数无法被重置。
  如果需要重置计数,请考虑使用 CyclicBarrier。
  CountDownLatch 是一个通用同步工具,它有很多用途。
  将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。
  用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
  CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。

 class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }

 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
      this.startSignal = startSignal;
      this.doneSignal = doneSignal;
   }
   public void run() {
      try {
        startSignal.await();
        doWork();
        doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

  另一种典型用法是,将一个问题分成 N 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。
  当所有的子部分完成后,协调线程就能够通过 await。(当线程必须用这种方法反复倒计数时,可改为使用 CyclicBarrier。)

class Driver2 { // ...
   void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     Executor e = ...

     for (int i = 0; i < N; ++i) // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));

     doneSignal.await();           // wait for all to finish
   }
 }

 class WorkerRunnable implements Runnable {
   private final CountDownLatch doneSignal;
   private final int i;
   WorkerRunnable(CountDownLatch doneSignal, int i) {
      this.doneSignal = doneSignal;
      this.i = i;
   }
   public void run() {
      try {
        doWork(i);
        doneSignal.countDown();
      } catch (InterruptedException ex) {} // return;
   }
   void doWork() { ... }
 }

  测试用例:

package add;

import java.util.concurrent.CountDownLatch;

/**
 * 减法计数器
 */
public class CountDownLatchDemo {

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

输出结果:

1 Go out
2 Go out
3 Go out
5 Go out
6 Go out
4 Go out
Close Door

进程已结束,退出代码0

  countDownLatch.countDown();/-1/
  countDownLatch.await();/等待计数器归零,然后再向下执行/

  每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await();就会被唤醒,继续执行!

CyclicBarrier

public class CyclicBarrierextends Object

  一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
  在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。
  因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
  CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。

class Solver {
   final int N;
   final float[][] data;
   final CyclicBarrier barrier;
   
   class Worker implements Runnable {
     int myRow;
     Worker(int row) { myRow = row; }
     public void run() {
       while (!done()) {
         processRow(myRow);

         try {
           barrier.await(); 
         } catch (InterruptedException ex) { 
return; 
         } catch (BrokenBarrierException ex) { 
return; 
         }
       }
     }
   }

   public Solver(float[][] matrix) {
     data = matrix;
     N = matrix.length;
     barrier = new CyclicBarrier(N, 
                                 new Runnable() {
                                   public void run() { 
                                     mergeRows(...); 
                                   }
                                 });
     for (int i = 0; i < N; ++i) 
       new Thread(new Worker(i)).start();

     waitUntilDone();
   }
 }

  每个 worker 线程处理矩阵的一行,在处理完所有的行之前,该线程将一直在屏障处等待。
  处理完所有的行之后,将执行所提供的 Runnable 屏障操作,并合并这些行。
  如果合并者确定已经找到了一个解决方案,那么 done() 将返回 true,所有的 worker 线程都将终止。
  如果屏障操作在执行时不依赖于正挂起的线程,则线程组中的任何线程在获得释放时都能执行该操作。
  为方便此操作,每次调用 await() 都将返回能到达屏障处的线程的索引。

if (barrier.await() == 0) {
     // log the completion of this iteration
   }

  对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。
  内存一致性效果:线程中调用 await() 之前的操作 happen-before 那些是屏障操作的一部份的操作,后者依次 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。

package add;

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

/**
 * 加法计数器
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        /**
         * 集齐9个勾玉
         * 召唤勾玉的线程
         */
        CyclicBarrier cyclicBarrier = new CyclicBarrier(9, () -> {
            System.out.println("召唤勾玉");
        });

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

输出结果:

Thread-1收集了1个勾玉
Thread-2收集了2个勾玉
Thread-3收集了3个勾玉
Thread-4收集了4个勾玉
Thread-5收集了5个勾玉
Thread-6收集了6个勾玉
Thread-7收集了7个勾玉
Thread-8收集了8个勾玉
Thread-9收集了9个勾玉
召唤勾玉

Semaphore

public class Semaphoreextends Objectimplements Serializable

  一个计数信号量。
  从概念上讲,信号量维护了一个许可集。
  如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。
  每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
  但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
  Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。

class Pool {
   private static final int MAX_AVAILABLE = 100;
   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

   public Object getItem() throws InterruptedException {
     available.acquire();
     return getNextAvailableItem();
   }

   public void putItem(Object x) {
     if (markAsUnused(x))
       available.release();
   }

   // Not a particularly efficient data structure; just for demo

   protected Object[] items = ... whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];

   protected synchronized Object getNextAvailableItem() {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (!used[i]) {
          used[i] = true;
          return items[i];
       }
     }
     return null; // not reached
   }

   protected synchronized boolean markAsUnused(Object item) {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (item == items[i]) {
          if (used[i]) {
            used[i] = false;
            return true;
          } else
            return false;
       }
     }
     return false;
   }

 }

  获得一项前,每个线程必须从信号量获取许可,从而保证可以使用该项。
  该线程结束后,将项返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。
  注意,调用 acquire() 时无法保持同步锁,因为这会阻止将项返回到池中。
  信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。
  将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。
  这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。
  按此方式使用时,二进制信号量具有某种属性(与很多 Lock 实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。
  在某些专门的上下文(如死锁恢复)中这会很有用。
  此类的构造方法可选地接受一个公平 参数。
  当设置为 false 时,此类不对线程获取许可的顺序做任何保证。
  特别地,闯入 是允许的,也就是说可以在已经等待的线程前为调用 acquire() 的线程分配一个许可,从逻辑上说,就是新线程将自己置于等待线程队列的头部。
  当公平设置为 true 时,信号量保证对于任何调用获取方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。
  注意,FIFO 排序必然应用到这些方法内的指定内部执行点。
  所以,可能某个线程先于另一个线程调用了 acquire,但是却在该线程之后到达排序点,并且从方法返回时也类似。
  还要注意,非同步的 tryAcquire 方法不使用公平设置,而是使用任意可用的许可。
  通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。
  为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。
  此类还提供便捷的方法来同时 acquire 和释放多个许可。
  小心,在未将公平设置为 true 时使用这些方法会增加不确定延期的风险。
  内存一致性效果:线程中调用“释放”方法(比如 release())之前的操作 happen-before 另一线程中紧跟在成功的“获取”方法(比如 acquire())之后的操作。

package add;

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

public class SemaphoreDemo {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        /*默认线程数量*/
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i < 7; i++) {
            new Thread(() -> {
                /*acquire() 得到*/
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); /*release()释放*/
                }
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:

1抢到车位
2抢到车位
3抢到车位
3离开车位
4抢到车位
2离开车位
5抢到车位
1离开车位
6抢到车位
4离开车位
5离开车位
6离开车位

进程已结束,退出代码0

  semaphore.acquire();获得,假设如果已经满了,等待,等待被释放为止!
  semaphore.release();释放,会将当前的信号量释放 + 1,然后隐藏等待的线程!
  作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!

读写锁

ReadWriteLock

public interface ReadWriteLock

  ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。
  只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。
  所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。
  也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。
  与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。
  虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。
  从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。
  在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。
  与互斥锁相比,使用读-写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。
  例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁的理想候选者。
  但是,如果数据更新变得频繁,数据在大部分时间都被独占锁,这时,就算存在并发性增强,也是微不足道的。更进一步地说,如果读取操作所用时间太短,则读-写锁实现(它本身就比互斥锁复杂)的开销将成为主要的执行成本,在许多读-写锁实现仍然通过一小段代码将所有线程序列化时更是如此。
  最终,只有通过分析和测量,才能确定应用程序是否适合使用读-写锁。
  尽管读-写锁的基本操作是直截了当的,但实现仍然必须作出许多决策,这些决策可能会影响给定应用程序中读-写锁的效果。
  这些策略的例子包括:
    在 writer 释放写入锁时,reader 和 writer 都处于等待状态,在这时要确定是授予读取锁还是授予写入锁。Writer 优先比较普遍,因为预期写入所需的时间较短并且不那么频繁。Reader 优先不太普遍,因为如果 reader 正如预期的那样频繁和持久,那么它将导致对于写入操作来说较长的时延。公平或者“按次序”实现也是有可能的。
    在 reader 处于活动状态而 writer 处于等待状态时,确定是否向请求读取锁的 reader 授予读取锁。Reader 优先会无限期地延迟 writer,而 writer 优先会减少可能的并发。
    确定是否重新进入锁:可以使用带有写入锁的线程重新获取它吗?可以在保持写入锁的同时获取读取锁吗?可以重新进入写入锁本身吗?
    可以将写入锁在不允许其他 writer 干涉的情况下降级为读取锁吗?可以优先于其他等待的 reader 或 writer 将读取锁升级为写入锁吗?
    当评估给定实现是否适合您的应用程序时,应该考虑所有这些情况。

package wr;

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

/**、
 * 读栈锁(写锁)
 * 共享锁(读锁)
 * ReadWriteLock
 * 读-读 可以共存
 * 读-写 不能共存
 * 写-写 不能共存
 */
public class ReadWriteLock {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        MyCache myCache = new MyCache();
        /*MyCacheLock myCache = new MyCacheLock();*/
        /*写入*/
        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        /*读取*/
        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }

    }
}

/**
 * 自定义程序
 */
class MyCache {
    private final Map<String, Object> map = new HashMap<>();

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

    }

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

    }
}

/**
 * 自定义程序
 */
class MyCacheLock {
    private final Map<String, Object> map = new HashMap<>();
    /*读写锁*/
    /*private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();*/
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /*存,写,写入的时候只希望只有一个线程写*/
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入成功");

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

    }

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

        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取成功");

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

    }
}

输出结果:

0写入0
0写入成功
1写入1
1写入成功
2写入2
2写入成功
3写入3
3写入成功
4写入4
4写入成功
5写入5
5写入成功
0读取0
0读取成功
1读取1
1读取成功
2读取2
2读取成功
3读取3
3读取成功
4读取4
4读取成功
5读取5
5读取成功

进程已结束,退出代码0

阻塞队列

  写入:如果队列满了,就必须阻塞等待
  取:如果队列是空的,必须阻塞等待生产

BlockingQueue

java.util.concurrent
接口 BlockingQueue

类型参数:
  E - 在此 collection 中保持的元素类型
所有超级接口:
  Collection, Iterable, Queue
所有已知子接口:
  BlockingDeque
所有已知实现类:
  ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue

public interface BlockingQueue<E>extends Queue<E>

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

学会使用队列

  添加、移除

四组API

方式抛出异常不会抛出异常,有返回值阻塞 等待超时等待
添加addoffer()put()offer(,)
移除removepoll()take()poll(,)
判断队列首elementpeek()

  1、抛出异常
  2、不会抛出异常
  3、阻塞等待
  4、超时等待

package bq;

import java.util.concurrent.ArrayBlockingQueue;

/*@SuppressWarnings("all")*/
public class Test {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        /**
         * Collection
         * list
         * set
         * BlockingQueue 不是新的东西
         */
        /*test1();*/
        test2();
    }

    /**
     * 抛出异常
     */
    public static void test1() {
        /*队列的大小*/
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        /*IllegalMonitorStateException:Queue full 抛出异常*/
        /*System.out.println(blockingQueue.add("d"));*/
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        /*java.util.NoSuchElementException 抛出异常
        System.out.println(blockingQueue.remove());*/
    }

    public static void test2() {
        /**
         * 队列的大小
         */
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println("=========================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());/*null*/
    }
}

输出结果:

true
true
true
=========================
b
a
c
null

进程已结束,退出代码0

SynchronousQueue 同步队列

  没有容量,进去一个元素,必须等待取出来之后,才能往里面放一个元素!put、take

package bq;

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

/**
 * 同步队列
 * 和其他的BlockingQueue不一样,SynchronousQueue不存储元素
 * put了一个元素,必须先从里面take取出来,否则不能在put进去值
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();/*同步队列*/
        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }
}

线程池**

池化技术

  三大方法、七大参数、四种拒绝资源
  程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术
  线程池、链接池、内存池、对象池///…
  池化技术:事先准备号一些资源,有人要用,就来我这里拿,用完之后还给我
线程池的好处:
  1、降低资源的消耗
  2、提高相应的速度
  3、方便管理。
  线程可以复用,线程可以控制最大并发数,管理线程

线程池:三大方法

package pool;

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

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

七大参数

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约等于21亿的数值
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

  本质ThreadPoolExecutor()

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                              int maximumPoolSize,//最大线程池大小
                              long keepAliveTime,//超时了 没有人会调用就会释放
                              TimeUnit unit,//超时的单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,创建线程的,一般不动
                              RejectedExecutionHandler handler//拒绝策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

四大拒绝

   AbortPolicy 容器满了,还有东西进来,不理会这个东西,抛出异常
   CallerRunsPolicy 哪来的去哪里,原路返回,不会抛出异常
   DiscardPolicy 队列满了,丢掉任务,不会抛出异常
   DiscardOldestPolicy 队列满了,尝试去和最早的竞争,也不会抛出异常

手动创建一个线程池

package pool;

import java.util.concurrent.*;

/**
 * Executors 工具类、三大方法
 * 使用了线程池之后,使用线程池来创建线程
 */
public class Demo01 {
    public static void main(String[] args) {
        System.out.println("输出结果:");
        System.out.println();
        /**
         * 8自定义线程池!工作 ThreadPoolExecutor
         * AbortPolicy 容器满了,还有东西进来,不理会这个东西,抛出异常
         * CallerRunsPolicy 哪来的去哪里,原路返回,不会抛出异常
         * DiscardPolicy 队列满了,丢掉任务,不会抛出异常
         * DiscardOldestPolicy 队列满了,尝试去和最早的竞争,也不会抛出异常
         */
        ExecutorService threadPool = new ThreadPoolExecutor(2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        try {
            /**
             * 最大承载:Deque + max
             * 超过 new RejectedThreadPoolExecutor
             */
            for (int i = 1; i <= 10; i++) {
                /*使用了线程池之后,使用线程池来创建线程*/
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /*线程池用完,程序结束,关闭线程池*/
            threadPool.shutdown();
        }
    }
}

小结

   自定义线程池!工作TheadPooLExecutor
   最大线程到底该如何定义
   1、CPU密集型,几核,就是几,可以保证CPU的效率最高!
   2、IO密集型》判断你程序中十分耗IO的线程
   程序 15个大型任务Io非常占用资源

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

四大函数式接口

   lamnda表达式、链式编程、函数式接口、Steam流计算

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

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

   超级多FunctionalInteface
   简化编程模型,在新版本的框架底层大量应用!
   foreach(消费者类型的函数式接口)

Function函数式接口

package function;

import java.util.function.Function;

/**
 * 函数型接口,有一个输入参数,有一个输出
 * 只要是函数式接口 就可以用 lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
        /**
         * 工具类:输出输入的值
         */
       /* Function function = new Function<String,String>() {
            @Override
            public String apply(String o) {
                return null;
            }
        };*/
        Function function = (str) -> {
            return str;
        };
        System.out.println(function.apply("asd"));
    }
}

Predicate 断定型接口

   为所有 FilteredRowSet 对象提供框架以描述其过滤器的标准接口。

public class Range implements Predicate {

       private Object lo[];
       private Object hi[];
       private int idx[];

       public Range(Object[] lo, Object[] hi, int[] idx) {
          this.lo = lo;
          this.hi = hi;
          this.idx = idx;
       }

      public boolean evaluate(RowSet rs) {
          CachedRowSet crs = (CachedRowSet)rs;
          boolean bool1,bool2;           
       
          // Check the present row determine if it lies
          // within the filtering criteria. 
      
          for (int i = 0; i < idx.length; i++) {
        
                if ((rs.getObject(idx[i]) >= lo[i]) && 
                  (rs.getObject(idx[i]) >= hi[i]) { 
                    bool1 = true; // within filter constraints
          } else {
            bool2 = true; // outside of filter constraints             
          } 
      }
      
      if (bool2) {
return false;
      } else {
return true;
      }
  }          
package function;

import java.util.function.Predicate;

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

Consumer 消费型接口

protected ImageConsumer consumer

   特定图像数据流的使用者,而此 ImageFilter 实例也是为这个使用者来过滤数据的。
   它不是在构造方法中进行初始化的,而是当 FilteredImageSource 正在为特定图像数据流创建此对象的惟一实例时,在 getFilterInstance() 方法调用期间进行初始化。

package function;

import java.util.function.Consumer;

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

Supplier 供给型接口**

package function;

import java.util.function.Supplier;

@SuppressWarnings("all")
public class Demo04 {
    public static void main(String[] args) {
        /*Supplier supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                System.out.println("get()");
                return 1024;
            }
        };*/
        Supplier supplier = () -> {
            return 1024;
        };
        System.out.println(supplier.get());
    }
}

Stream流式计算

社么是流式计算

   大数据:存储+计算
   集合、MySql本质就是存储东西的;
   九三级都应该交给流来操作!

现在有五个用户,筛选:
   * 1、ID必须大于偶数
   * 2、年龄必须大于23岁
   * 3、用户名转为大写字母
   * 4、用户名倒着排序
   * 5、只输出一个用户

package stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 有参,无参构造,get、set、toString方法!
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;
}

package stream;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/*
 *
 * 现在有五个用户,筛选:
 * 1、ID必须大于偶数
 * 2、年龄必须大于23岁
 * 3、用户名转为大写字母
 * 4、用户名倒着排序
 * 5、只输出一个用户
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "a", 22);
        User u3 = new User(3, "a", 23);
        User u4 = new User(4, "a", 24);
        User u5 = new User(5, "a", 25);
        /*集合就是存储*/
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        /*计算交给Steam流*/
        list.stream()
                /*ID必须大于偶数*/
                .filter(u -> {
                    return u.getId() % 2 == 0;
                })
                /*年龄必须大于23岁*/
                .filter(u -> {
                    return u.getAge() > 23;
                })
                /*用户名转为大写字母*/
                .map(u -> {
                    return u.getName().toUpperCase(Locale.ROOT);
                })
                /*用户名倒着排序*/
                .sorted((uu1, uu2) -> {
                    return uu1.compareTo(uu2);
                })
                /*只输出一个用户*/
                .limit(1)
                .forEach(System.out::println);
    }
}

ForkJoin

什么是ForkJoin

   ForkJoin在JDK1.7,并行执行项目!提高效率,大数据量!
   大数据:Map reduce(把大数据拆分为小任务)

ForkJoin的工作特点:工作窃取

package fokjoin;

import java.util.concurrent.RecursiveTask;

/**
 * 求和计算的任务!
 * 如何使用ForkJoin
 * 1、forkjoin 通过它来执行
 * 2、计算任务forkjoinPool.excute(ForkJoinTask task)
 * 3.计算类必须继承 ForkJoinTask
 */
public class ForkJoinDemo<L extends Number> extends RecursiveTask<Long> {
    /*临界值*/
    private final Long temp = 10000L;
    private final Long start;
    private final Long end;

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

    @Override
    protected Long compute() {
        /*如果小于临界值,则采用普通的计算方式*/
        if ((end - start) < temp) {
            /*分支合并计算*/
            Long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {/*如果大于临界值,则采用forkjoin的计算方式*/
            long middle = (start + end) / 2; /*中间值*/
            ForkJoinDemo<Number> task1 = new ForkJoinDemo<Number>(start, middle);
            task1.fork();/*拆分任务,把任务压入线程队列*/
            ForkJoinDemo<Number> task2 = new ForkJoinDemo<Number>(middle + 1, end);
            task2.fork();/*拆分任务,把任务压入线程队列*/

            return task1.join() + task2.join();
        }
    }
}

package fokjoin;

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(); sum=499999999500000000时间14880
         * test2(); sum=500000000500000000时间13488
         * test3(); sum=500000000500000000时间416
         */

    }

    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 1L; i < 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + "时间" + (end - start));
    }

    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + "时间" + (end - start));
    }
    public static void test3() {
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + "时间" + (end - start));
    }
}

异步回调

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

package futue;

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

/**
 * 异步调用:AJax
 * 异步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*发起一个请求,没有返回值的异步回调*/
        /*CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "runAsync=>Void");

        });
        System.out.println("11111111");
        completableFuture.get();*//*阻塞获取执行结果*/
        /**
         * 有返回值的 supplyAsync 异步回调
         * ajax,成功和失败的回调
         * 返回的是错误信息
         */

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
            int i = 10 / 0;
            return 1024;
        });
        completableFuture.whenComplete((t, u) -> {
            System.out.println("t=>" + t);
            System.out.println("u=>" + u);/*错误信息:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero*/
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233;/*可以获取到错误的返回结果*/
        }).get();
    }
}

JMM

请你谈谈对Volatile的理解

Volatile是Java虚拟机提供轻量级的同步机制
   1、保证可见性
   2、不保证原子性**
   3、禁止指令重排

什么是JMM

   JMM:Java内存模型,不存在的东西,概念!约定!

关于JMM的一些同步的约定:
   1、线程解锁的,必须把共享变量立刻刷新回主存。
   2、线程加锁前,必须读取贮存中的最新值到工作内存中!
   3、加锁和解锁是同意一把锁
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
   lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
   unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
   read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
   load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
   use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
   assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
   store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
   write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
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

1、保证可见性

package com.ly.tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    /**
     * 不加volatile程序就会死循环
     * 保证程序的可见性
     */
    private volatile static int num = 0;

    public static void main(String[] args) {
        new Thread(() -> {/*线程1启动,线程1对主内存的变化是不知道的*/
            while (num == 0) {

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }

}

2、不保证原子性

   原子性:不可分割
   线程A在执行任务的时候是不能被打扰的,也不能被分割,要么同时成功,要么同时失败

package com.ly.tvolatile;

@SuppressWarnings("all")
/**
 * 不保证原子性
 */
public class VDemo02 {
    /**
     * volatile 不保证原子性l
     */
    private volatile static int num = 0;

    private static void add() {
        num++;
    }

    public static void main(String[] args) {
        /*理论上num结果应该为2万*/
        for (int i = 1; i < 20; i++) {
            new Thread(() -> {
                for (int j = 1; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

原子类为什么这么高级

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

package com.ly.tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

@SuppressWarnings("all")
/**
 * 不保证原子性
 */
public class VDemo02 {
    /**
     * volatile 不保证原子性l、
     * 原子类的Integer
     */
    private volatile static AtomicInteger num = new AtomicInteger();


    private static void add() {
        /*num++;*//*不是一个原子性操作*/
        num.getAndIncrement();/*AndIncrement+1 方法,CAS*/
    }

    public static void main(String[] args) {
        /*理论上num结果应该为2万*/
        for (int i = 1; i < 20; i++) {
            new Thread(() -> {
                for (int j = 1; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
} 

这些类的地层都是直接和操作系统挂钩!在内存中修改值!Unasfe类是一个很特殊的存在

指令重排

   什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
   源代码->编译器优化的重排->内存系统也会重排->执行
   处理器在进行指令重排的时候,考虑:数据之间的依赖性!

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

我们所期望的,1234 但是可能执行的时候就变成2134 1324
可不肯是 4123 

   可能造成影响的结果:a b c d 四个值默认都是0;

线程A线程B
x = ay = b
b = 1a = 2

   正常的结果 : x = 0 ; y = 0 ;但是可能由于指令重排

线程A线程B
x = aa = 2
b = 1y = b

   指令重排导致的诡异结果 : x = 2 ; y = 1

非计算机专业

volatile可以避免指令重排:
   内存屏障;CPU指令;作用:
      1、保证特定的操作的执行顺序!
      2、可以保证某些变量的内存可见性(利用这些特性,可以保证volatile的可见性)

彻底玩转单例模式

饿汉式

package com.ly.single;

/**
 * 单例模式:饿汉式
 */
public class Hungey {
    private final static Hungey HUNGEY = new Hungey();

    private Hungey() {

    }

    public static Hungey getInstance() {
        return HUNGEY;
    }
}

DCL懒汉式

public class LazyMan {
    private static LazyMan lazyMan;

     /*private static LazyMan lazyMan;*/

     private LazyMan() {

     }
 	 /*双重检测锁模式 懒汉式单例 DCL懒汉式*/
     public static LazyMan getInstance() {
         if (lazyMan == null) {
             lazyMan = new LazyMan();
         }
         return lazyMan;
     }
     
package com.ly.single;

public class LazyMan {
    private static LazyMan lazyMan;

    /* private static LazyMan lazyMan;

     private LazyMan() {

     }

     public static LazyMan getInstance() {
         if (lazyMan == null) {
             lazyMan = new LazyMan();
         }
         return lazyMan;
     }*/
    private LazyMan() {
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    /**
                     * 不是一个原子性操作
                     * 1、分配内存空间
                     * 2、执行构造方法
                     * 3、把这个对象指向这个空间
                     *
                     * 期望执行:123
                     * 真实执行:132
                     * A     B      刺水lazyMan还没有完成构造
                     */
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }

    /*多线程并发*/
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类

package com.ly.single;

/**
 * 静态内部类
 */
public class Holder {
    private Holder() {

    }

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

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

单例不安全,因为有反射

枚举

package com.ly.single;

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

/*enum是一个什么?本身也是一个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;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
        /*NoSuchMethodException: com.ly.single.EnumSingle.<init>()*/
        System.out.println(instance1);
        System.out.println(instance2);

    }
}

枚举的最终反编译

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

深入理解CAS

什么是CAS

package com.ly.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    /**
     * CAS compareAndSet
     *
     * @param args
     */
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        /**
         * 期望、更新
         * public final boolean ccompareAndSet(int expect, int update)
         * 如果我期望的值达到了,那么就更新,否则,就不更新,CAS,是CPU的并发原语!
         */

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        atomicInteger.getAndIncrement();
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }

}

Unsafe类

private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
public static Unsafe getUnsafe() {
    Class cc = sun.reflect.Reflection.getCallerClass(2);
    if (cc.getClassLoader() != null)
        throw new SecurityException("Unsafe");
    return theUnsafe;
}

CAS:ABA问题(狸猫换太子)

   CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
缺点:
   1、循环会耗时
   2、一次性只能保证一个共享变量的原子性
   3、ABA问题

原子引用

带版本号的原子操作

   Integer使用了对象缓存机制,默认范围是-128 ~ 127 ,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象fe配新的内存空间

package com.ly.cas;

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

public class CASDemo {
    /**
     * CAS compareAndSet
     * AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
     *
     * @param args
     * 正常在业务操作,这里面比较的都是一个个对象
     */
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

    public static void main(String[] args) {
        /**
         * int INtege
         * CAS compareAndSet : 比较并交换
         */
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();/*获得版本号*/
            System.out.println("a1 => " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            /**
             *
             */
            System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2 => " + stamp);
            System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3 => " + stamp);
        }, "a").start();
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();/*获得版本号*/
            System.out.println("b1 => " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));
            System.out.println("b1 => " + atomicStampedReference.getStamp());
        }, "b").start();
    }

}

各种锁的理解

1、公平锁、非公平锁

   公平锁:非常公平,不能插队,必须先来后到!
   非公平锁:非常不公平,可以插队(默认都是非公平)

public ReentrantLock() {
        sync = new NonfairSync();
    }
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、可重入锁

Synchronized

package com.ly.lock;

/**
 * Synchronized
 */
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");
    }
}

lock

package com.ly.lock;

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

/**
 * Synchronized
 */
public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        long
        new Thread(() -> {
            phone.sms();
        }, "A").start();
        new Thread(() -> {
            phone.sms();
        }, "B").start();
    }
}

class Phone2 {
    Lock lock = new ReentrantLock();

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

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

3、自旋锁

package com.ly.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁
 */
public class SpinlockDemo {
    /**
     * int 0
     * THread null
     */
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    /**
     * 加锁
     */
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "==> mylock");
        /**
         * 自旋锁
         */
        while (atomicReference.compareAndSet(null, thread)) {

        }
    }

    /**
     * 解锁
     */
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "==> myUnlock");
        atomicReference.compareAndSet(thread, null);
    }
}

测试

package com.ly.lock;

import java.util.concurrent.TimeUnit;

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        /*ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        reentrantLock.unlock();*/
        /**
         * 底层使用的自旋锁CAS
         */
        SpinlockDemo lock = new SpinlockDemo();
        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T1").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T2").start();
        lock.myLock();
        lock.myUnLock();
    }
}

4、死锁

死锁是什么

   死锁测试,怎么排除死锁

package com.ly.lock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockA";
        new Thread(new MyThead(lockA, lockB), "T1").start();
        new Thread(new MyThead(lockB, lockA), "T1").start();
    }
}

class MyThead implements Runnable {
    private String lockA;
    private String lockB;

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

    public MyThead() {
    }

    @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);

            }
        }
    }
}

解决问题

   1、使用jps定位进程号
   2、使用jstack找到死锁问题
   3、查看日志
   4、查看堆栈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

游荡在雾中的霾

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

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

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

打赏作者

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

抵扣说明:

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

余额充值