多线程到juc

1、线程创建方式

1.1、Thread 

1.2、Runnable

package com.aliyun.server;

/**
 * 多个线程操作同一个对象
 */
public class TestThread implements Runnable{

    private int ticketNum = 10;

    @Override
    public void run() {
        while (true){
            if (ticketNum <= 1){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNum-- + "张票");

        }
    }

    public static void main(String[] args)  {
        TestThread ticket = new TestThread();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"👩‍").start();
        new Thread(ticket,"黄牛").start();

    }
}



1.2.1龟兔赛跑

package com.aliyun.server;

/**
 * 龟兔赛跑
 */
public class TestThread implements Runnable{

    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {

            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("🐰")){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //判断比赛是否结束
            boolean flag = gameOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了第" + i +"步");
        }
    }

    //判断是否完成比赛
    private boolean gameOver(int i) {

        if (i == 100){
            winner = Thread.currentThread().getName();
            System.out.println("胜利者是"+winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args)  {
        TestThread ticket = new TestThread();

        new Thread(ticket,"🐢").start();
        new Thread(ticket,"🐰").start();


    }
}



1.3.实现Callable接口

好处:

 可以定义返回值

 可以抛出异常

2、静态代理

package com.aliyun.server;

/**
 * 静态代理
 */
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}

//真实角色,新郎结婚
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("要结婚了");
    }
}

//代理角色,婚庆公司帮你结婚
class WeddingCompany implements Marry{

    //代理谁?真实目标角色
    private Marry target;

    public WeddingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        //结婚前
        before();
        this.target.HappyMarry();//这就是真实对象
        //结婚后
        after();
    }

    private void before(){
        System.out.println("结婚前布置婚礼");
    }
    private void after(){
        System.out.println("结婚后收钱");
    }
}

 2.1静态代理总结:

        真实对象和代理对象都要实现同一个接口

        代理对象要代理真实的角色(代码中的target)

2.2好处:

        代理对象可以做很多真实对象做不了的事情

        真实对象专注做自己的事情        

2.3多线程中静态代理的应用

new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        });

Thread为代理对象,Runnable为真实对象。公共实现了run()方法

3、Lamda表达式

 推导:

        原来的方式:

/**
 * 推导Lambda
 */
public class StaticProxy {
    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();
    }
}

interface ILike{
    void lambda();
}

class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

静态内部内:

/**
 * 推导Lambda
 */
public class StaticProxy {
    static class Like implements ILike{

        @Override
        public void lambda() {
            System.out.println("i like lambda");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();
    }
}

interface ILike{
    void lambda();
}

  局部内部类:

/**
 * 推导Lambda
 */
public class StaticProxy {


    public static void main(String[] args) {
         class Like implements ILike{

            @Override
            public void lambda() {
                System.out.println("i like lambda");
            }
        }

        ILike like = new Like();
        like.lambda();
    }
}

interface ILike{
    void lambda();
}

匿名内部类:

/**
 * 推导Lambda
 */
public class StaticProxy {


    public static void main(String[] args) {
         new ILike(){
             @Override
             public void lambda() {
                 System.out.println("i like lambda");
             }
         }.lambda();
    }
}

interface ILike{
    void lambda();
}

lambda:

public class StaticProxy {


    public static void main(String[] args) {
        ILike like = ()-> System.out.println("i like");
        like.lambda();
    }
}

interface ILike{
    void lambda();
}

4、线程的五大状态

 

5、停止线程 

/**
 * 停止线程
 * 1。建议线程正常停止,利用次数不建议死循环
 * 2。建议使用标识位,设置一个标识位
 * 3。不要使用stop或者destroy等果实或者jdk不建议使用的方法
 */
public class Stop implements Runnable {
    //设置标识位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run Thread" + i);
        }

    }

    //设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        Stop testStop = new Stop();
        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {
            if (i == 900){
                System.out.println(i);
                testStop.stop();
                System.out.println("线程停止");
            }
        }
    }
}

6、 线程休眠

7、线程礼让

/**
 * 线程礼让
 *礼让不一定会成功,看cpu心情
 */
public class Stop{

    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+":开始执行");
    }
}

 

8、Join(某条线程强制执行) 

/**
 * Join
 *
 */
public class Stop implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程VIP来了-->"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Stop stop = new Stop();
        Thread thread = new Thread(stop);
        thread.start();

        //主线程
        for (int i = 0; i < 100; i++) {
            if (i==10){
                //插队
                thread.join();
            }
            System.out.println("mian线程-->" + i);
        }
    }
}

9、线程状态观测 

10、线程优先级

/**
 * 测试线程优先级
 */
public class Stop{

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();

        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);

        //先设置优先级再启动
        t1.start();//默认是5

        t2.setPriority(1);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);//最大值10
        t3.start();

        t4.setPriority(6);
        t4.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
    }
}

优先级不是绝对的,只是概率会大一些

11、守护(daemon)线程


/**
 * 测试线程优先级
 */
public class Stop{

    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread godT = new Thread(god);
        godT.setDaemon(true);//默认是false(不是守护线程)
        godT.start();

        new Thread(you).start();


    }
}

//上帝:守护线程
class God implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("上帝啊");
        }
    }
}

//你:用户线程
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("你的一生");
        }
        System.out.println("===goodbye word===");
    }
}

 12、并发

 12.1、线程同步

13、线程同步安全和不安全案例 

        13.1.不安全的买票

/**
 * 线程安全和不安全测试
 */
public class Stop{

    public static void main(String[] args) {
        BuyTicket stop = new BuyTicket();
        new Thread(stop,"小黑").start();
        new Thread(stop,"小白").start();
        new Thread(stop,"小红").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNum = 10;

    private boolean flag = true;

    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void buy() throws InterruptedException {
        if (ticketNum<=0){
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
        ticketNum --;
        Thread.sleep(200);
    }
}

14、线程同步方法

14.1.同步方法弊端 

14.2.不安全买票改造

public class Stop{

    public static void main(String[] args) {
        BuyTicket stop = new BuyTicket();
        new Thread(stop,"小黑").start();
        new Thread(stop,"小白").start();
        new Thread(stop,"小红").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNum = 10;

    private boolean flag = true;

    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void buy() throws InterruptedException {
        if (ticketNum<=0){
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
        ticketNum --;
        Thread.sleep(200);
    }
}

 

14.3.同步块: 

个人理解:obj就是谁进行了增删改操作就写哪个对象

15、 死锁

举例子:

/**
 * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
 */
public class DeadLock{
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑凉");
        Makeup g2 = new Makeup(0,"白雪");

        new Thread(g1,"灰姑凉").start();
        new Thread(g2,"白雪").start();
    }
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup implements Runnable{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();


    int choice;//选择
    String girlName;//使用化妆品的人

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run()  {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获取口红的锁
                System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){//一秒后想获得镜子
                    System.out.println(Thread.currentThread().getName()+":获得了镜子的锁");
                }
            }
        }else {
            synchronized (mirror) {//获取镜子的锁
                System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
                Thread.sleep(1000);
                synchronized (lipstick){//一秒后想获得口红
                    System.out.println(Thread.currentThread().getName()+":获得了口红的锁");
                }
            }
        }
    }
}

 解决:

package com.aliyun.server;

/**
 * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
 */
public class DeadLock{
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑凉");
        Makeup g2 = new Makeup(0,"白雪");

        new Thread(g1,"灰姑凉").start();
        new Thread(g2,"白雪").start();
    }
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup implements Runnable{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();


    int choice;//选择
    String girlName;//使用化妆品的人

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run()  {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获取口红的锁
                System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
                Thread.sleep(1000);
            }
            synchronized (mirror){//一秒后想获得镜子
                System.out.println(Thread.currentThread().getName()+":获得了镜子的锁");
            }
        }else {
            synchronized (mirror) {//获取镜子的锁
                System.out.println(Thread.currentThread().getName() + ":获取到了口红的锁");
                Thread.sleep(1000);
            }
            synchronized (lipstick){//一秒后想获得口红
                System.out.println(Thread.currentThread().getName()+":获得了口红的锁");
            }
        }
    }
}

不相互抱对方的锁就可以了

死锁产生条件:

 16、Lock锁

ReentrantLock:可重入锁 

/**
 * lock锁
 */
public class TestLock {

    public static void main(String[] args) {
        BuyTicket stop = new BuyTicket();
        new Thread(stop,"小黑").start();
        new Thread(stop,"小白").start();
        new Thread(stop,"小红").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNum = 10;

    private boolean flag = true;

    //定义lock锁
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (flag){
            try {
                lock.lock();//加锁
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                lock.unlock();//解锁
            }

        }
    }

    private void buy() throws InterruptedException {
        if (ticketNum<=0){
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum +"几张票");
        ticketNum --;
        Thread.sleep(200);
    }
}

 synchronized和Lock的对比

17、线程通信

17.1.线程协作:生产者消费者模式

 

 

18、线程池

  

/**
 * 测试线程池
 */
public class TestPool {

    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //参数:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        //2。执行
        service.execute(new BuyTicket());
        service.execute(new BuyTicket());
        service.execute(new BuyTicket());
        service.execute(new BuyTicket());

        //3.关闭连接
        service.shutdownNow();
    }
}

class BuyTicket implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

 

 19、什么是JUC 

20、进程和线程 

进程:一个程序 。比如QQ.exe,一个进程往往包含多个线程,至少包含一个

java默认包含几个线程?两个:main线程和gc线程

线程:操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位

21、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++
    private native void start0();

java是运行在虚拟机上的无法直接操作硬件,start底层调用的是 native方法,实则是c++

21、并发和并行

并发:(多线程操作同一个资源):cpu一核,多个线程快速交替。比如食堂一个窗口,多人排队

并行:(多个人一起行走):cpu多核,多个线程同时执行。比如食堂多个窗口,一人一个窗口

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

23、sleep和wait区别

1、来自不同的类,wait来自object类,sleep来自Thread

2、锁的释放:wait会释放锁。sleep不会释放,抱着锁睡觉

3、使用的范围不一样:wait:必须在同步代码块中使用。sleep可以在任何地方睡

4、是否需要捕获异常:wait不需要捕获异常。sleep需要捕获异常

24、JUC Lock锁 

        24.1.Lock接口

                api:        

               

                   实现类:

        24.1.1 ReentrantLock(可重入锁)源码:

公平锁:十分公平:先来后到。比如一个3h的线程排前面,一个3s的排后面。必须等3h的执行完才能执行3s的

非公平锁:十分的不公平:可以插队(默认的) 

传统卖票方式(synchronized)

/**
 * 传统卖票方式:synchronized
 */
public class test {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        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();
    }
}
class Ticket{
    //属性,方法
    private int number = 50;

    //卖票方式
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第" + (number--)+"张票");
        }
    }
}

        改进卖票方式(lock)

public class test {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        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();
    }
}
class Ticket{
    //属性,方法
    private int number = 50;

    Lock lock = new ReentrantLock();
    //卖票方式
    public synchronized void sale(){
        lock.lock();//加锁
        try {
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第" + (number--)+"张票");
            }
        }finally {
            lock.unlock();//解锁
        }

    }
}

24.1.2.synchronized和Lock锁的区别

1.synchronized是内置的java关键字,Lock是一个java类

2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到锁

3.synchronized会自动释放,Lock需要手动释放

4.synchronized线程会一直等待,Lock就不一定会等待下去

5.synchronized是可重入锁,不可中断的非公平锁。Lock是可重入锁,可以设置为非公平和公平锁

6. synchronized适合锁少量代码同步问题,Lock适合锁大量的同步代码

25.生产者和消费者问题

 25.1.传统版本的代码实现

/**
 * 线程之间的通信问题
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1 的同时 B num-1 保持num守恒
 */
public class test {

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

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

    }
}

//等待、业务、通知
class Data{
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        if(number != 0){
            //等待
            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 C B D 4个线程会存在虚假唤醒

 解决,使用while判断:if判断不会停,while判断一但拿到锁就不会停

/**
 * 线程之间的通信问题
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1 的同时 B num-1 保持num守恒
 */
public class test {

    public static void main(String[] args) throws Exception{
        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();

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

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

    }
}

//等待、业务、通知
class Data{
    private int number = 0;

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

        25.2.JUC版代码实现

/**
 * 线程之间的通信问题
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1 的同时 B num-1 保持num守恒
 */
public class test {

    public static void main(String[] args) throws Exception{
        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();

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

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

    }
}

//等待、业务、通知
class Data{
    private int number = 0;

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    //+1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0){
                //等待
                condition.await();
            }
            number ++;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            //通知其他线程,我+1完毕了
            condition.signal();

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

 Condition可以做到精准唤醒,比如说A线程可以直接唤醒B线程类似

26.八锁现象

如果判断锁的是谁?

        26.1.如下代码 思考:先打印打电话还是发短信?

public class test {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        Lock lock = new ReentrantLock();

        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

//等待、业务、通知
class Phone{
   public synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

 结论:synchronized锁的是对象的调用者,谁调用我就锁谁,两个方法用的是同一个锁(phone),谁先拿到谁先执行。公平锁

        26.2.如下代码 思考:先打印发短信还是hello?

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

        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

//等待、业务、通知
class Phone{
   public synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

hello方法没有锁!不是同步方法,不受锁的影响。因为sendSms方法有休眠,所以hello方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行

       26.3. 如下代码 思考:先打印打电话还是发短信?

public class test {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(()->{
            try {
                phone1.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

//等待、业务、通知
class Phone{
   public synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

}

分析:phone1和phoen2是两个对象,所以是两把锁,不需要排队。因为sendSms方法有休眠,所以call方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行

 26.4. 如下代码 思考:先打印打电话还是发短信?(两个方法变成了静态方法stattic修饰的)

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

        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone{
   public static synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

分析:加上static后锁的就是全局唯一的class对象(phone.class)代码验证如下 26.5

26.5.如下代码 思考:先打印打电话还是发短信?(一个静态方法,一个非静态方法)

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

        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone{
   public static synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

 分析:phone.sendSms()锁的是方法调用者,phone.call锁的是phone.class对象,因为sendSms方法有休眠,所以call方法一直先执行,如果sendSms方法没休眠就是先调用哪个方法哪个方法就先执行

26.6. 如下代码 思考:先打印打电话还是发短信?(两个静态方法stattic修饰的,两个对象调用不同的方法)

public class test {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(()->{
            try {
                phone1.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone{
   public static synchronized void sendSms() throws InterruptedException {
       TimeUnit.SECONDS.sleep(4);
        System.out.println("发短信");
    }

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

分析:加上static后锁的就是全局唯一的class对象(phone.class),phone1和phoen2都是Phone.class对象

27.CopyOnWriteArrayList

public class test {
    public static void main(String[] args) throws InterruptedException {
        List<String>  list = new ArrayList<>();

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

    }
}

ConcurrentModificationException:并发修改异常

结论:ArrayList在并发的情况下是不安全的 

解决方案:

1.使用Vector(),不推荐

2.List<String> list = Collections.synchronizedList(new ArrayList<>());

3.juc类下的CopyOnWreArrayList:写入时候复制 COW是计算机程序设计领域的一种优化策略,多个线程调用的时候,list,读取的时候,固定的写入(覆盖),在写入的时候避免覆盖,造成数据问题

4.CopyOnWreArrayList比Vector效率要高使用的是lock锁,Vector使用的是的synchronized

28.CopyOnWriteArraySet

public class test {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();

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

    }
}

 结论:HashSet在并发的情况下是不安全的  

解决方法:

1.Set<Object> set = Collections.synchronizedSet(new HashSet<>());

2.CopyOnWriteArraySet<Object> set = new CopyOnWriteArraySet<>();

HashSet底层就是HashMap:本质就是hashMap的key,是无法重复的

29.ConcurrentHashMap 

public class test {
    public static void main(String[] args) {
        HashMap<Object, Object> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                 System.out.println(map);
             },String.valueOf(i)).start();
        }

    }
}

 结论:HashMap在并发的情况下是不安全的  

解决:

1.Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());

2.ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();

ConcurrentHashMap原理:

        1.8之前是采用的分段锁

        1.8之后采用的是cas

30.Callable

1.可以有返回值

2.可以抛出异常

3.方法不同 ,call()方法

 代码实现:

public class test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);//适配类

        new Thread(futureTask,"A").start();
        System.out.println(futureTask.get());

    }
}

class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("call");
        return "12345";
    }
}

 31.常用的辅助类(CountDownlatch,CyclicBarrier,Semaphore)

31.1.CountDownlatch(减法计数器)

public class test {
    public static void main(String[] args) throws InterruptedException {
        //总数是6,必须要执行任务的时候,再使用
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"走了");
                countDownLatch.countDown();//-1
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();//等待计数器归零,再执行后续操作
        System.out.println("Close Door");
    }
}

原理:

countDownLatch.countDown();//-1

countDownLatch.await();//等待计数器归零,再执行后续操作

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

31.2.CyclicBarrier(加法计数器)

public class test {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤神龙成功");
        });

        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+":收集到第"+temp+"颗龙珠");
                try {
                    cyclicBarrier.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

31.3.Semaphore(信号量、限流 )

public class test {
    public static void main(String[] args) throws InterruptedException {

        //线程数量,限流
        Semaphore semaphore = new Semaphore(3);//模拟三个停车位

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

        }
    }
}

 原理:

semaphore.acquire()获得,加入已经满了,等待被释放为止
semaphore.release():释放,会将当前的信号量释放,然后唤醒等待的线程

作用:多个共享资源互斥的作用!并发限流,控制最大的线程数

32.读写锁(ReadWriteLock)

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

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

        //读取
        for (int i = 0; i < 5; 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()+"写入ok");
    }

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

 从结果来看有写入未完成被插队写入的情况出现,是不正常的。

用读写锁改良后:

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

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

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

class MyCache{

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

    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    //存、写
    public void put(String key,Object value){

        lock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入ok");
        }finally {
            lock.writeLock().unlock();
        }
    }

    //取、读
    public void get(String key){

        lock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取ok");
        }finally {
            lock.readLock().unlock();
        }
    }
}

 读-读:可以共存

读-写:不可以共存

写-写:不可以共存

33.阻塞队列

 BlockingQueue:阻塞队列

什么情况下我们会使用 阻塞队列:多线程和线程池

 33.1.四组API:

1.抛出异常

新增:add()

删除:remove()

查看队首元素:peek()

public class test {
    public static void main(String[] args){
        test1();
    }

    public static void test1(){
        //队列大小为3
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));

        //报错:Queue full,超出了队列大小
//        System.out.println(blockingQueue.add("d"));

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

        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());

        //报错:NoSuchElementException 队列为空,没有可删除的元素
        //System.out.println(blockingQueue.remove());
    }
}

2.不抛出异常

新增:offer()

弹出、删除:poll()

查看队首元素:element()

public class test {
    public static void main(String[] args){
        test1();
    }

    public static void test1(){
        //队列大小为3
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));

        // false
        System.out.println(blockingQueue.offer("d"));

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

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

        // null
        System.out.println(blockingQueue.poll());
    }
}

3.阻塞

新增:put()

移除:take()

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    public static void test1() throws InterruptedException {
        //队列大小为3
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");

        blockingQueue.put("d");//队列没位置了,一直阻塞,直到存进去为止

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        
        System.out.println(blockingQueue.take());//没有这个元素,一直阻塞。直到拿到为止

    }
}

4.超时等待(实际这个用的多)

新增:offer()

弹出、删除:poll(,,)

查看队首元素:element(,)

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    public static void test1() throws InterruptedException {
        //队列大小为3
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS));//等待2s就退出

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

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//等待2s就退出
    }
}

 33.2. 同步队列

进去一个元素,必须等待取出来之后,才能再往里面放一个元素!(容量为1)

put、take

34、线程池

池化技术:实现准备好一些资源,有人要用,就来我这里拿,用完后还给我

好处:

1、降低资源消耗

2、提高响应速度

3、方便管理

34.1.Executors

34.1.1.Executors.newSingleThreadExecutor():单个线程

public class test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newSingleThreadExecutor();

        try {
            for (int i = 0; i < 5; i++) {
                final int tem = i;
                service.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            service.shutdownNow();//关机
        }

    }

 34.1.2.Executors.newFixedThreadPool():固定大小线程池

public class test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(4);

        try {
            for (int i = 0; i < 5; i++) {
                final int tem = i;
                service.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            service.shutdownNow();//关机
        }

    }
}

 

 34.1.3.Executors.newCachedThreadPool():遇强则强,可伸缩的

public class test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newCachedThreadPool();

        try {
            for (int i = 0; i < 10; i++) {
                final int tem = i;
                service.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            service.shutdownNow();//关机
        }

    }
}

35.七大参数 

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }


    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }


    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

通过以上源码分析可以知道,这些方法底层实质上都是调用的ThreadPoolExecutor()方法

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                              int maximumPoolSize,//最大核心线程池大小
                              long keepAliveTime,//超时时间
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,创建线程的
                              RejectedExecutionHandler handler//拒绝策略    
                        
                            ) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

35.1.七大参数讲解

 

图解:其中一二号窗口为核心线程数,会一直开放。三四五号窗口加上一二号窗口是最大线程数,但是不会一直开开放,会设置超时等待时间,当过了超时等待时间,没人办理业务,三四五窗口会关闭。当队列和核心线程数都满了,在有线程进来的时候才会逐个开放。候客区就是队列的长度。当最大线程数加上队列都满了会触发拒绝策略(四种)。

最大承载 = 队列+最大线程数

35.1.1.四种拒绝策略

35.1.1.1.AbortPolicy:超过最大承载会抛出异常
public class test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,//核心线程数
                5,//最大线程数
                3,//超时时间
                TimeUnit.SECONDS,//超时时间单位
                new LinkedBlockingQueue<>(3),//队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );
        //最大承载数:max(5) + queue(3)
        try {
            for (int i = 1; i <= 11 ; i++) {
                final int tem = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdownNow();//关机
        }

    }
}
35.1.1.2.CallerRunsPolicy:哪来的去哪里(由main线程执行多余的)
public class test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,//核心线程数
                5,//最大线程数
                3,//超时时间
                TimeUnit.SECONDS,//超时时间单位
                new LinkedBlockingQueue<>(3),//队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.CallerRunsPolicy()//拒绝策略->哪来的去哪里
        );
        //最大承载数:max(5) + queue(3)
        try {
            for (int i = 1; i <= 11 ; i++) {
                final int tem = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdownNow();//关机
        }
    }
}
35.1.1.3.DiscardPolicy:哪来的去哪里(不会抛出异常,满了会丢掉任务)
public class test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,//核心线程数
                5,//最大线程数
                3,//超时时间
                TimeUnit.SECONDS,//超时时间单位
                new LinkedBlockingQueue<>(3),//队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.DiscardPolicy()//不会抛出异常,满了会丢掉任务
        );
        //最大承载数:max(5) + queue(3)
        try {
            for (int i = 1; i <= 11 ; i++) {
                final int tem = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdownNow();//关机
        }
    }
}

35.1.1.4.DiscardOldestPolicy:尝试和最早的线程竞争,也不会抛出异常

public class test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,//核心线程数
                5,//最大线程数
                3,//超时时间
                TimeUnit.SECONDS,//超时时间单位
                new LinkedBlockingQueue<>(3),//队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.DiscardOldestPolicy()//尝试和最早的线程竞争,也不会抛出异常
        );
        //最大承载数:max(5) + queue(3)
        try {
            for (int i = 1; i <= 11 ; i++) {
                final int tem = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdownNow();//关机
        }
    }
}

36.CPU密集型和IO密集型

36.1.池的最大大小怎么去设置

36.1.1.CPU密集型

机器是几核,就是几,可以保持cpu的效率最高

获取机器核数代码

Runtime.getRuntime().availableProcessors()

代码改造

public class test {
    public static void main(String[] args) throws InterruptedException {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,//核心线程数
                Runtime.getRuntime().availableProcessors(),//最大线程数
                3,//超时时间
                TimeUnit.SECONDS,//超时时间单位
                new LinkedBlockingQueue<>(3),//队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.DiscardOldestPolicy()//尝试和最早的线程竞争,也不会抛出异常
        );
        //最大承载数:max(5) + queue(3)
        try {
            for (int i = 1; i <= 11 ; i++) {
                final int tem = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName()+"->"+tem);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            threadPool.shutdownNow();//关机
        }
    }
}

36.1.2.IO密集型

会判断程序中十分耗IO的线程。然后最大线程数一般设置是这个值的两倍

37.ForkJoin

37.1.什么是forkJoin

forkJoin 在JDK1.7,并行执行任务!提高效率。大数据量!(把大任务拆为小人物)

37.2.特点

工作窃取:维护的都是双端队列

B执行完了,A会把B窃取过来执行,提高工作效率

38.异步回调(Future)

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

38.1.没有返回值

/**
 * 并行执行
 * 成功回调
 * 失败回调
 */
public class test{
    public static void main(String[] args) throws Exception {
        CompletableFuture<Void> completableFuture =CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println("111");

        completableFuture.get();//获取阻塞执行结果
    }
}

38.2.有返回值

public class test{
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> completableFuture =CompletableFuture.supplyAsync(()->{
            return "1024";
        });

        System.out.println("111");

        completableFuture.get();//获取阻塞执行结果
    }
}

39.JMM

39.1.什么是jmm

java内存模型,概念

39.2.JMM的同步约定

1.线程解锁前,必须立刻把共享变量刷回主存

图解:线程拷贝主存的东西,而不是直接从主存拿来用。当拷贝的被修改,就必须马上把共享变量同步回主线程

2.线程加锁前,必须读取主存中的最新值到工作内存。

3.加锁和解锁是同一把锁

39.3.线程、工作内存、主内存8种操作

·read 读取:作用于主内存,将共享变量从主内存传送到线程的工作内存中。
·load 载入:作用于工作内存,把 read 读取的值放到工作内存中的副本变量中。
·store 存储:作用于工作内存,把工作内存中的变量传送到主内存中。
·write 写入:作用于主内存,把从工作内存中 store 传送过来的值写到主内存的变量中。
·use 使用:作用于工作内存,把工作内存的值传递给执行引擎,当虚拟机遇到一个需要使用这个变量的指令时,就会执行这个动作。
·assign 赋值:作用于工作内存,把执行引擎获取到的值赋值给工作内存中的变量,当虚拟机栈遇到给变量赋值的指令时,就执行此操作。
·lock锁定: 作用于主内存,把变量标记为线程独占状态。
·unlock解锁: 作用于主内存,它将释放独占状态。

public static void main(String[] args) throws Exception {

        new Thread(()->{
            while (num == 0){
                System.out.println("a");
            }
        }).start();

        TimeUnit.SECONDS.sleep(2);

        num =1;
        System.out.println(num);

    }

问题:程序不知道主内存的值已经被修改(用Volatile解决)

40.Volatile

是java虚拟机提供轻量级的同步机制

40.1.特点

1.保证可见性

public class test{
    //不加volatile就会死循环
    private volatile static int num = 0;

    public static void main(String[] args) throws Exception {

        new Thread(()->{
            while (num == 0){
                System.out.println("a");
            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        num =1;
        System.out.println(num);

    }
}

2.不保证原子性

原子性:不可分割

比如线程在执行任务的时候是不能被打扰的,也不能被分割。要么同时成功,要么同时失败。

public class test{

    private volatile static int num = 0;

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

    public static void main(String[] args) throws Exception {

        //理论上结果应该为2万
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){//获取线程数量 2:java程序默认有main线程和gc线程
            Thread.yield();//礼让
        }
        System.out.println(num);
    }
}

可以看出,结果不对,体现了不保证原子性

如果不加synchronized和lock怎样保证原子性呢?

num ++ 不是原子性的操作,分为了三步

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

public class test{

    private static AtomicInteger num = new AtomicInteger();

    public static void main(String[] args){

        //理论上结果应该为2万
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    num.getAndIncrement();
                }
            }).start();
        }

        while (Thread.activeCount()>2){//获取线程数量 2:java程序默认有main线程和gc线程
            Thread.yield();//礼让
        }
        System.out.println(num);
    }
}

原子类可以保证原子性,这些类的底层都和操作系统挂钩!在内存中修改值!Unsafe类是个很特殊的存在

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

3.禁止指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的

int x = 1; // 1

int y = 2; // 2

x = x + 5;  //3

y = x + y; // 4

我们期望的执行顺序:1234

但是有可能的执行顺序: 2134 1324

有没有可能是 4123呢?不可能

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

可能造成影响的结果:abxy这四个值默认都是0:

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

正常的结果:x=0;y=0;但是有可能b=1先执行等情况从而改变x,y的初始值。

41.彻底玩转单例模式

恶汉式单例

一开始就加载

public class Hungry {

    private Hungry(){

    }

    private final static Hungry HUNGRY = new Hungry();

    public static  Hungry getInstance(){
        return HUNGRY;
    }

}
public class Hungry {

    //一开始就把所有东西全部加载进来,会导致内存浪费
    private byte[] bytes1 = new byte[1924*1024];
    private byte[] bytes2 = new byte[1924*1024];
    private byte[] bytes3 = new byte[1924*1024];
    private byte[] bytes4 = new byte[1924*1024];

    Hungry(){

    }

    private final static Hungry HUNGRY = new Hungry();

    public static  Hungry getInstance(){
        return HUNGRY;
    }
}

懒汉式单例

需要的时候才加载,以下单线程下是ok的,多线程下会有问题
/**
 * 懒汉式:需要的时候才加载,单线程下是ok的,多线程下会有问题
 */
public class LazyMan {

    private LazyMan(){

    }

    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if (lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}
public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+":ok");
    }

    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if (lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

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

41.2.1.双重检测锁模式的懒汉式单里 DCL懒汉式

public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+":ok");
    }

    private volatile static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if (lazyMan == null){
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }

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

42.深入理解CAS

什么是cas: 比较并交换

CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环(底层是自选锁)

缺点:

        1.循环耗时

        2.一次只能保证一个共享变量的原子性

        3.ABA问题

public class LazyMan {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2023);
        //如果我期望的值(2023)达到了,那么就更新,否则不更新
        boolean a = atomicInteger.compareAndSet(2023, 2024);
        System.out.println(atomicInteger.get());
        System.out.println(a);

        boolean b = atomicInteger.compareAndSet(2023, 2024);
        System.out.println(atomicInteger.get());
        System.out.println(b);
    }
}

Unsafe类

43.ABA问题

图解问题说明:主内存被来A = 1,A线程和B线程同时拿到了a=1.但是B线程从1换到了3,然后又改成了1.导致A线程不知道主线程的值已经改过了(狸猫换太子)

public class LazyMan {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2023);
        //如果我期望的值(2023)达到了,那么就更新,否则不更新
        
        //============捣乱的线程===============
        boolean a = atomicInteger.compareAndSet(2023, 2024);
        System.out.println(a);

        boolean b = atomicInteger.compareAndSet(2024, 2023);
        System.out.println(b);
        
        //===========期望的线程=================
        boolean c = atomicInteger.compareAndSet(2023, 666);
        System.out.println(a);
        
    }
}

44.原子引用解决ABA问题

带版本号的原子操作:

public class LazyMan {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(3, 1);

        new Thread(()->{
            int stamp = reference.getStamp();//获取版本号
            System.out.println("a1=>" + stamp);

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

            System.out.println(reference.compareAndSet(3,
                    4,
                    reference.getStamp(),//版本号
                    reference.getStamp() + 1//版本号+1
            ));

            System.out.println("a2=>" + reference.getStamp());


            System.out.println(reference.compareAndSet(4,
                    3,
                    reference.getStamp(),//版本号
                    reference.getStamp() + 1//版本号+1
            ));

            System.out.println("a3=>" + reference.getStamp());
        },"a").start();

        new Thread(()->{
            int stamp = reference.getStamp();//获取版本号
            System.out.println("b1=>"+stamp);

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

            System.out.println(reference.compareAndSet(3, 6, stamp, stamp + 1));
            System.out.println("b2=>"+reference.getStamp());
        },"b").start();
    }
}

 45.各种锁的理解

公平锁、非公平锁

公平锁:非常公平,不能够插队,必须先来后到

非公平锁:非常不公平,可以插队(默认都是非公平)

可重入锁

递归锁

自旋锁

不断的尝试,直到成功为止

//自旋锁
public class LazyMan {

    AtomicReference<Object> 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);
    }

    public static void main(String[] args) {
        LazyMan lock = new LazyMan();
        lock.myLock();
        lock.myUnLock();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值