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

  • 分析之前存在的问题
    在这里插入图片描述
    我们的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();
    }
}

  • 这里复习我们的多线程的状态
    在这里插入图片描述
 public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,//新建

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

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

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

(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定义的也可以
    在这里插入图片描述
  • 还有static方法
    在这里插入图片描述

其实很简单,只有一个方法的接口就是函数式接口,此时就可以使用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锁实现多线程
    在这里插入图片描述
//资源类
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线程)
    在这里插入图片描述
    注意标志位

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

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

    }

}

先启动哪个方法
在这里插入图片描述

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

    }

}

在这里插入图片描述

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

    }

}

在这里插入图片描述

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



    }

}

在这里插入图片描述

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



    }

}

在这里插入图片描述

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



    }

}

在这里插入图片描述

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


    }

}

在这里插入图片描述

(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)代码演示

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

  • 错误示范
    在这里插入图片描述
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)原理

在这里插入图片描述
如果把资源设置为1,那就相当于使用synchronized。

4、ReadWriteLockDemo

(1)概念理解

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

  • 对ReadWriteLock理解就是:读的时候不能锁,写的时候必须锁。
    在这里插入图片描述

(2)手写一个缓存

class MyCache {
    private volatile Map<String,Object> 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<String,Object> 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<String,Object> 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)阻塞队列

在这里插入图片描述

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

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

(2)用处

在这里插入图片描述

(3)分析继承结构

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

(4)核心API与代码测试

  • 核心API
    在这里插入图片描述

  • 添加代码测试
    在这里插入图片描述
    在这里插入图片描述

  • remove删除方法调用
    在这里插入图片描述
    在这里插入图片描述

  • element检查队列中队首
    在这里插入图片描述

  • offer,往队列里面添加数据
    在这里插入图片描述
    在这里插入图片描述

  • poll取出数据
    在这里插入图片描述

  • put方法
    在这里插入图片描述
    线程不会结束
    在这里插入图片描述

  • take阻塞等待生产
    在这里插入图片描述
    等待生产
    在这里插入图片描述

  • offer(e,timeout)
    在这里插入图片描述
    等待四秒
    在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值