JUC学习笔记第一篇(总体第三篇)

一、对之前内容简单复习

1、JUC介绍

(1)概念

  • 是在java.util.concurrent(juc)
    总体有三个包java.util.concurrent和java.util.concurrent.atomic以及java.util.concurrent.locks包。
  • 理解其实就是java并发编程

2、回顾点内容

(1)多线程回顾(卖票程序)

  • 回顾卖票程序(企业级的)
package cn.mldn.juc.Ticket;

/**
 * 高内聚低耦合的类
 */
class Ticket {
    private int ticket = 30;

    public synchronized   void setTicket() {
        //1、判断
        while (ticket > 0) {
            //2、卖票
            ticket --;
            System.out.println(Thread.currentThread().getName() + "线程卖出一张票" + ";剩余" + ticket + "张票");
        }

    }

}

/**
 * 企业级多线程(题目,3个卖票员卖出30张票
 * 1、在高内聚低耦合的前提下,线程 操作 资源类
 * 2、
 */
public class TicketTest {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程A");

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程B");

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程C");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

  • 分析之前存在的问题
    JUC学习笔记第一篇(总体第三篇)_第1张图片
    我们的synchronized相当于一个表锁,把我们的整体都锁住,性能不好。我们进一步细致的控制的话,就要使用lock了。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 高内聚低耦合的类
 */
class Ticket {
    private int ticket = 30;

    private Lock lock = new ReentrantLock();


    public void setTicket() {
        lock.lock();
        try {
            //1、判断
            if (ticket > 0) {
                //2、卖票
                ticket --;
                System.out.println(Thread.currentThread().getName() + "线程卖出一张票" + ";剩余" + ticket + "张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

}

/**
 * 企业级多线程(题目,3个卖票员卖出30张票
 * 1、在高内聚低耦合的前提下,线程 操作 资源类
 * 2、
 */
public class TicketTest {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程A");

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程B");

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.setTicket();
            }
        },"线程C");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

  • 这里复习我们的多线程的状态
    JUC学习笔记第一篇(总体第三篇)_第2张图片
 public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,//新建

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,//就绪状态

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,//阻塞状态

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * 
    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

(2)Lambda表达式

  • 无参数
interface foo {
    public void hello() ;
}

public class LambdaExpress {

    public static void main(String[] args) {
        foo foo = () -> {
            System.out.println("hello");
        };
        foo.hello();
    }
}

  • 有参数
interface foo {
    /*public void hello() ;*/
    public int hello2(int a) ;
}

public class LambdaExpress {

    public static void main(String[] args) {
        foo foo = (int a) -> {
            System.out.println("hello");
            return a;
        };
        foo.hello2(1);
    }
}

  • 两个参数
@FunctionalInterface
interface foo {
    /*public void hello() ;*/
    public int hello2(int a,int b) ;
}

public class LambdaExpress {

    public static void main(String[] args) {
        foo foo = (int a,int b) -> {
            System.out.println("hello");
            return a+b;
        };
        foo.hello2(1,2);
    }
}
  • 有自己default定义的也可以
    JUC学习笔记第一篇(总体第三篇)_第3张图片
  • 还有static方法
    JUC学习笔记第一篇(总体第三篇)_第4张图片

其实很简单,只有一个方法的接口就是函数式接口,此时就可以使用Lambda表达式。

(3)生产者消费者模式复习

  • 生产者和消费者模型时线程间进行通信的原始模型,这个很重要,在很多地方都能应用起来的。

  • 看第一种情况

public class ConsumerANDProduce {

    //不管怎么样,一定要记住,涉及到了多线程,就会是线程操作资源类

    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                airConditioner.increment();
            }
        },"线程A").start();

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

//资源类
class AirConditioner {
    private int number = 0;

    public synchronized void increment() {
        //1、判断
        if(number != 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、干活
        number++;
        System.out.println("" + Thread.currentThread().getName() + "线程,完成生产");

        //3、通知
        this.notifyAll();
    }

    public synchronized void decrement() {
        //1、判断
        if(number == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、干活
        number--;
        System.out.println("" + Thread.currentThread().getName() + "线程,完成消费");

        //3、通知
        this.notifyAll();
    }
}
  • 使用while进行判断(防止虚假唤醒)
    当有多个生产者线程和多个消费者同时进行操作数据的时候,此时就可能会虚假唤醒其它的,就可以用while进行等待。{用while循环的话,可以判断后,当你判断的被打断后,会再次拉回来进行判断}
public synchronized void increment() {
        //1、判断
        while (number != 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、干活
        number++;
        System.out.println("" + Thread.currentThread().getName() + "线程,完成生产" + number);

        //3、通知
        this.notifyAll();
    }

    public synchronized void decrement() {
        //1、判断
        while (number == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、干活
        number--;
        System.out.println("" + Thread.currentThread().getName() + "线程,完成消费" + number);

        //3、通知
        this.notifyAll();
    }

原理分析:当我们使用两个线程,一个去生成,一个去消费,此时number不管怎么样,一个生成,唤醒另外一个,此时不管怎么样都不会出现问题。但是当你多个线程的时候,就比如两个加线程A和B,两个减线程C和D,

假如如果A线程进去后,判断,如果被wait进行等待后,它在等待,当其他消费线程消费后,唤醒生产线程生产,如果两个生产线程都进行唤醒,哦豁了撒,两个线程都生产,不行(我们的目的是生成一个消费一个)。当用了while判断后,你判断了,如果下次被唤醒了,它要进行重新判断。这就是为什么能写while就防止虚假唤醒的原因了。
  • 使用Lock锁实现多线程
    JUC学习笔记第一篇(总体第三篇)_第5张图片
//资源类
class AirConditioner {
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment() {
        lock.lock();
        try {
            //1、判断
            while (number != 0) {
                try {
                    condition.await();
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //2、干活
            number++;
            System.out.println("" + Thread.currentThread().getName() + "线程,完成生产" + number);

            //3、通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() {

        lock.lock();
        try {
            //1、判断
            while (number == 0) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //2、干活
            number--;
            System.out.println("" + Thread.currentThread().getName() + "线程,完成消费" + number);

            //3、通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        
    }
}
  • 使用Lock实现精确打击(精确唤醒,比如A线程执行后,我要叫醒B线程)
    JUC学习笔记第一篇(总体第三篇)_第6张图片
    注意标志位

class ShareResource {
    int number = 1;

    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    private Condition conditionC = lock.newCondition();

    private void AA() {
        lock.lock();
        try {
            //1、判断
            while (number != 1) {
                conditionA.await();
            }
            //2、干活
            for (int i = 0; i < 5; i++) {
                System.out.println("AA线程打印");
            }
            //3、通知
            number = 2;//设置标志位
            conditionB.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void BB() {
        lock.lock();
        try {
            //1、判断
            while (number != 2) {
                conditionA.await();
            }
            //2、干活
            for (int i = 0; i < 10; i++) {
                System.out.println("BB线程打印");
            }
            //3、通知
            number = 3;//设置标志位
            conditionC.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    private void CC() {
        lock.lock();
        try {
            //1、判断
            while (number != 3) {
                conditionA.await();
            }
            //2、干活
            for (int i = 0; i < 5; i++) {
                System.out.println("CC线程打印");
            }
            //3、通知
            number = 3;//设置标志位
            conditionA.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

(4)再次回顾一下Callable接口

  • Callable与Runnable区别
    JUC学习笔记第一篇(总体第三篇)_第7张图片
    JUC学习笔记第一篇(总体第三篇)_第8张图片
  • 实现我们的Callable
    JUC学习笔记第一篇(总体第三篇)_第9张图片
    不能直接传Callable接口,而是要通过和Runnable接口与Callable有联系的某一个类和方法。
    JUC学习笔记第一篇(总体第三篇)_第10张图片
    它的实现类FutureTask【这里就是我们的多态的实现案例了】
    JUC学习笔记第一篇(总体第三篇)_第11张图片
    在这里插入图片描述
  • 获取我们返回值
    JUC学习笔记第一篇(总体第三篇)_第12张图片
  • Callable细节理解
    • 其实内部就是另外一个线程在工作,它进行处理的是,根据简单的先处理,难的留在后续处理。
    • get方法一般放在最后一行 ,因为只要你调用这个get了,程序就认为你造成等待的时间,会被阻塞起来,会影响性能。
    • 不管启动多少个线程,都是处理的同一个对象
      JUC学习笔记第一篇(总体第三篇)_第13张图片
      它内部会缓存。

3、多线程锁(8锁问题)

1)第一锁

(1)现象

class Phone {
    public synchronized void wangzry() throws Exception {
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();

        Thread.sleep(1000);
        new Thread(() -> {
            try {
                phone.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();

    }

}

先启动哪个方法
JUC学习笔记第一篇(总体第三篇)_第14张图片

(2)原理

并不是A和B同时进入到了资源类里面,线程启动并不是按着我们的意愿来启动的,底层用了native定义的。我们强制性的要求A线程先启动(因为B被停了),所以上面个方法肯定要先被启动。

  • 原理:synchronized锁的是当前资源类,只要用synchronized定义的普通方法,整个类被锁住,简单理解起来,就是你new的对象,不可能有两个及以上的线程访问。

2)第二锁

(1)现象

class Phone {
    public synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();

        
        new Thread(() -> {
            try {
                phone.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();

    }

}

JUC学习笔记第一篇(总体第三篇)_第15张图片

(2)原理

  • 同样的为什么我们的juedqs没有被暂停时间也会在后面等呢,原因和上面的 一样,同一时刻,有且仅有一个线程能访问资源类的当前对象,所以我们A线程在访问了,那你B线程也得等。

(3)第一和第二锁总结

在方法上加了synchronized锁的是对象,即对象锁,同一时刻有且仅有一个线程能访问。

3)第三锁

(1)现象

package cn.mldn.juc.cap;



class Phone {
    public synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();


        new Thread(() -> {
            try {
                phone.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();

        new Thread(() -> {
            try {
                phone.sayHello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程C").start();

    }

}

JUC学习笔记第一篇(总体第三篇)_第16张图片

(2)原理

其实很好理解,普通方法(没有加上synchronized的方法),是不会和锁扯上关系的。

4)第四锁

(1)现象

package cn.mldn.juc.cap;



class Phone {
    public synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();


        new Thread(() -> {
            try {
                phone1.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();



    }

}

JUC学习笔记第一篇(总体第三篇)_第17张图片

(2)原理

他们之间没有任何竞争关系,不存在关系了。所以肯定是这个答案了。

5)第五锁

(1)现象

class Phone {
    public static synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public static synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();


        new Thread(() -> {
            try {
                phone.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();



    }

}

JUC学习笔记第一篇(总体第三篇)_第18张图片

(2)原理

  • 原理:static拥有部分,是属于这个类的,是这个类的模板。
  • 加了static后,使用了我们的synchronized的锁后,它锁的是类型,结合第六锁来看,好理解。

6)第六锁

(1)现象



class Phone {
    public static synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public static synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();

        Thread.sleep(1000);

        new Thread(() -> {
            try {
                phone1.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();



    }

}

JUC学习笔记第一篇(总体第三篇)_第19张图片

(2)原理

  • 我们的phone1去调用的是juedqs,static把定义的锁方法,此时整个类模板都锁住了,所以,就算你上面个王者荣耀先启动都不管我是,乖乖的等我线程A执行完了,你B线程再执行。

(3)五和六锁的总结

我们用static后,锁的不再是一个个的对象,而是锁的整个Phone.class类型。

7)第七锁

(1)现象

package cn.mldn.juc.cap;



class Phone {
    public static synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();


        new Thread(() -> {
            try {
                phone.juedqs();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程B").start();

        new Thread(() -> {
            try {
                phone.sayHello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程C").start();


    }

}

JUC学习笔记第一篇(总体第三篇)_第20张图片

(2)原理

  • 好理解了吧,有了static后,我锁住了整个模板,此时只能我访问完了,你后面的才能访问。而我们的sayHello和juedqs方法就凭争取,那个先就哪个。

8)第八锁

(1)现象

package cn.mldn.juc.cap;



class Phone {
    public static synchronized void wangzry() throws Exception {
        Thread.sleep(4000);
        System.out.println("打开王者荣耀");
    }
    public synchronized void juedqs() throws Exception {
        System.out.println("打开绝地求生");
    }

    public  void sayHello() throws Exception {
        System.out.println("打开sayHello");
    }
}


public class Test {

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

        //1、资源类
        Phone phone = new Phone();
        Phone phone1 = new Phone();

        new Thread(() -> {
            try {
                phone.wangzry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程A").start();


       

        new Thread(() -> {
            try {
                phone1.sayHello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"线程C").start();


    }

}

在这里插入图片描述

(2)原理

我们的两部手机,虽然你们是来自于同一个模板,但是我们两个间互相不影响。所以你该调调你的,不关我事。

二、几个问题

1、控制线程顺序(CountDownLatchDemo)

(1)代码演示

比如我们的七个同血晚上下晚自习后,要离开教室,其中有一个人是班长,班长必须是最后一个把灯管了走。

  • 错误示范
    JUC学习笔记第一篇(总体第三篇)_第21张图片
public class test1 {

    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "离开");
            },"" + i).start();
        }
        System.out.println(Thread.currentThread().getName() + "班长离开");


    }
}

  • 正确处理
public class test1 {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "离开");
                countDownLatch.countDown();
            },"" + i).start();
        }
        //班长要先卡着,只有上面的启动完了再走。
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "班长离开");
    }
}

(2)原理

在这里插入图片描述

2、控制多个线程等待(CyclicBarrierDemo)

  • 其实可以理解为要等10个人到齐了再开会。
 public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10,() -> {
            System.out.println("集合完毕,可以开会");
        });

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println(String.valueOf(finalI) + "员工到会");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },i + "").start();
        }
    }

3、控制多线程的并发数(SemaphoreDemo)

(1)代码演示

  • 上面的demo中,都是多个线程去抢占同一个资源。现在要想模拟多对多,比如我们的七个车抢占4个车位【然后后面的三个要等前面的走了才能进去,而且只能一个一个进去】。
  • demo演示
public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);//模拟资源,有三个空车位

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();//占用空车位
                    System.out.println(Thread.currentThread().getName() + "线程,抢占到车位");
                    Thread.sleep(3);//占用3秒
                    System.out.println(Thread.currentThread().getName() + "线程,离开");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();//并且释放之前的空车位
                }
            },"" + i).start();
        }
    }

(2)原理

JUC学习笔记第一篇(总体第三篇)_第22张图片
如果把资源设置为1,那就相当于使用synchronized。

4、ReadWriteLockDemo

(1)概念理解

  • 如果还是对lock不了解,可以自己去找找资源了解了解。

  • 对ReadWriteLock理解就是:读的时候不能锁,写的时候必须锁。
    JUC学习笔记第一篇(总体第三篇)_第23张图片

(2)手写一个缓存

class MyCache {
    private volatile Map map = new HashMap<>();

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

    }
    public void get(String key) {
        System.out.println("" + Thread.currentThread().getName() + "开始读取");
        map.get(key);
        System.out.println("" + Thread.currentThread().getName() + "读取成功");

    }

}

(3)模拟问题

class MyCache {
    private volatile Map map = new HashMap<>();

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

    }
    public void get(String key) {
        System.out.println("" + Thread.currentThread().getName() + "开始读取");
        map.get(key);
        System.out.println("" + Thread.currentThread().getName() + "读取成功");

    }

}

public class test2 {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        //五个写
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.put("" + finalI, finalI+ "");
            },finalI + "").start();
        }

        //五个读
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get(finalI + "");
            },finalI + "").start();
        }
    }
}

  • 执行结果
0写入数据
3写入数据
4写入数据
4写入成功
1写入数据
2写入数据
1写入成功
3写入成功
0写入成功
2写入成功
0开始读取
0读取成功
1开始读取
1读取成功
2开始读取
4开始读取
4读取成功
3开始读取
2读取成功
3读取成功

我们没法保证数据的完整性(即原子性)

(4)解决问题

class MyCache {
    private volatile Map map = new HashMap<>();
    private ReadWriteLock readWriteLock = null;

    public void put(String key,Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println("" + Thread.currentThread().getName() + "写入数据");
            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() + "开始读取");
            map.get(key);
            System.out.println("" + Thread.currentThread().getName() + "读取成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }

}

5、阻塞队列(BlockingQueueDemo)

(1)阻塞队列

JUC学习笔记第一篇(总体第三篇)_第24张图片

  • 当队列是空的时候,从队列里面获取元素的操作将被阻塞,直到其他线程往队列里面添加了元素。
  • 当队列是满的时候,往队列里面方法元素的操作将被阻塞,直到里面元素被消费了才会能添加。

可以比喻为海底捞火锅店的候客区。

(2)用处

JUC学习笔记第一篇(总体第三篇)_第25张图片

(3)分析继承结构

JUC学习笔记第一篇(总体第三篇)_第26张图片
JUC学习笔记第一篇(总体第三篇)_第27张图片
JUC学习笔记第一篇(总体第三篇)_第28张图片
JUC学习笔记第一篇(总体第三篇)_第29张图片
JUC学习笔记第一篇(总体第三篇)_第30张图片

(4)核心API与代码测试

  • 核心API
    JUC学习笔记第一篇(总体第三篇)_第31张图片

  • 添加代码测试
    JUC学习笔记第一篇(总体第三篇)_第32张图片
    JUC学习笔记第一篇(总体第三篇)_第33张图片

  • remove删除方法调用
    JUC学习笔记第一篇(总体第三篇)_第34张图片
    JUC学习笔记第一篇(总体第三篇)_第35张图片

  • element检查队列中队首
    JUC学习笔记第一篇(总体第三篇)_第36张图片

  • offer,往队列里面添加数据
    JUC学习笔记第一篇(总体第三篇)_第37张图片
    JUC学习笔记第一篇(总体第三篇)_第38张图片

  • poll取出数据
    JUC学习笔记第一篇(总体第三篇)_第39张图片

  • put方法
    JUC学习笔记第一篇(总体第三篇)_第40张图片
    线程不会结束
    JUC学习笔记第一篇(总体第三篇)_第41张图片

  • take阻塞等待生产
    JUC学习笔记第一篇(总体第三篇)_第42张图片
    等待生产
    JUC学习笔记第一篇(总体第三篇)_第43张图片

  • offer(e,timeout)
    JUC学习笔记第一篇(总体第三篇)_第44张图片
    等待四秒
    JUC学习笔记第一篇(总体第三篇)_第45张图片

你可能感兴趣的