《JUC并发编程 - 基础篇》JUC概述 | Lock接口 | 线程间通信 | 多线程锁 | 集合线程安全(二)

简介: 《JUC并发编程 - 基础篇》JUC概述 | Lock接口 | 线程间通信 | 多线程锁 | 集合线程安全

3、Java8之lambda表达式复习

Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。


Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称

为 Lambda 操作符或剪头操作符。它将 Lambda 分为 两个部分:


左侧:指定了 Lambda 表达式需要的所有参数

右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能

代码演示


package com.atguigu.thread;
@FunctionalInterface
interface Foo{
    // public void sayHello() ;   
    public int add(int x,int y);
    //函数式接口可以有多个 default实现
    //JDK1.8之后 接口里面可以有方法的实现
    default int div(int x,int y) {
        return x/y;
    }
    //可以有多个静态函数
    public static int sub(int x,int y) {
        return x-y;
    }
}
/**
 * 
 * @Description: Lambda Express-----> 函数式编程
 * 1 口诀:拷贝小括号(形参列表),写死右箭头 ->,落地大括号 {方法实现}
 * 2 什么是Lambda:有且只有一个public方法(@FunctionalInterface注解增强定义)
 * 3 JDK1.8之后可以有default方法默认实现
 * 4 可以有静态方法实现
 */
public class LambdaDemo
{
    public static void main(String[] args)
    {
        //使用匿名内部类方式调用接口中的方法方式
        // Foo foo = new Foo() {
        //     @Override
        //     public void sayHello() {
        //         System.out.println("Hello!!");
        //     }
        // foo.sayHello();
        //使用Lambda方式
        Foo foo = (x,y)->{
            System.out.println("Hello!! lambda !!");
            return x+y;
        };
        int result = foo.add(3,5);
        System.out.println("******result="+result);
        System.out.println("******result div="+foo.div(10, 2));
        System.out.println("******result sub="+Foo.sub(10, 2));
    }
}

4、线程间通信

线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。

我们来基本一道面试常见的题目来分析

场景: 两个线程,一个线程对当前数值加1,另一个线程对当前数值减1,要求 用线程间通信

4.1 synchronized实现


package com.rg.sync;
//第一步 创建资源类,定义属性和操作方法
class Share {
    //初始值
    private int number = 0;
    //+1的方法
    public synchronized void incr() throws InterruptedException {
        //第二步 判断 干活 通知
        while(number != 0) { //判断number值是否是0,如果不是0,等待
            this.wait(); //在哪里睡,就在哪里醒
        }
        //如果number值是0,就+1操作
        number++;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }
    //-1的方法
    public synchronized void decr() throws InterruptedException {
        //判断
        while(number != 1) {
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }
}
/**
 * 
 * @Description:
 * 现在两个线程,
 * 可以操作初始值为零的一个变量,实现一个线程对该变量加1,一个线程对该变量减1,交替,来10轮。 
 * Java里面如何进行工程级别的多线程编写
 * 1 多线程编程模板(套路)-----上
 *     1.1  在高内聚低耦合的场景下:线程    操作    资源类  
 * 2 多线程编程模板(套路)-----中
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 * 3 多线程编程模板(套路)-----下
 *   防止虚假唤醒用while,不用if
 * 虚假唤醒例子:坐飞机,机长广播飞机上有不安全物品,需要全体人员下来然后工作人员检查.检查完之后,需要重新安检上飞机...
*/
public class ThreadDemo1 {
    //第三步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        //创建线程
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }
}

**结果分析:**当只有AA,BB线程时,一切运行正常, AA::1和BB::0 轮流执行

02cdec30e55f1b43e40beb2811aa42ea.png

当增加CC、DD线程时,会出现虚假唤醒的情况,

d904158430632f874ffef30f8b721465.png

原因分析:

e1f032338f0c8794e817c16c0a26d350.png


69ebfc6aa2baa27233d818ec21f9823f.png

4.2 Lock 方案

package com.rg.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author lxy
 * @version 1.0
 * @Description
 * @date 2022/4/25 17:30
 */
//第一步 创建资源类,定义属性和操作方法
class Share{
    private int number = 0;
    //创建Lock
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    //+1
    public void incr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (number != 0){
                condition.await();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName()+" :: "+number);
            //通知
            condition.signalAll();
        }finally {
            //解锁
            lock.unlock();
        }
    }
    //-1
    public void decr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
           //判断
            while (number!=1){
                condition.await();
            }
           //干活
            number--;
            System.out.println(Thread.currentThread().getName()+" :: "+number);
            //通知
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
/**
 * 
 * @Description:
 * 现在两个线程,
 * 可以操作初始值为零的一个变量,实现一个线程对该变量加1,一个线程对该变量减1,交替,来10轮。 
 * Java里面如何进行工程级别的多线程编写
 * 1 多线程编程模板(套路)-----上
 *     1.1  在高内聚低耦合的场景下:线程    操作    资源类  
 * 2 多线程编程模板(套路)-----中
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 * 3 多线程编程模板(套路)-----下
 *   防止虚假唤醒用while,不用if
*/
public class ThreadDemo2 {
    //第三步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        //创建线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//加1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();//加1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//加1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();//加1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }
}

4.3 线程间定制化调用通信

三个线程启动,要求如下:

AA打印5次,BB打印10次,CC打印15次.

接着

AA打印5次,BB打印10次,CC打印15次

…来10轮

实现代码

package com.rg.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author lxy
 * @version 1.0
 * @Description
 * @date 2022/4/27 16:01
 */
//第一步 创建资源类
class ShareResource{
    //定义标志位
    private int flag = 1;//1  AA  2 BB  3 CC
    //创建Lock锁
    private Lock lock = new ReentrantLock();
    //创建三个Condition,也就是三把钥匙
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();
    //打印5次,参数第几轮
    public void print5(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (flag!=1){
                //等待
                c1.await();
            }
            //干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" : 轮数: "+loop);
            }
            //通知
            flag = 2;
            c2.signal();//通知BB线程(精准通知)
        }finally {
            //释放锁
            lock.unlock();
        }
    }
    //打印10次,参数第几轮
    public void print10(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (flag!=2){
                //等待
                c2.await();
            }
            //干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" : 轮数: "+loop);
            }
            //通知
            flag = 3;
            c3.signal();//通知CC线程(精准通知)
        }finally {
            //释放锁
            lock.unlock();
        }
    }
    //打印15次,参数第几轮
    public void print15(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (flag!=3){
                //等待
                c3.await();
            }
            //干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" : 轮数: "+loop);
            }
            //通知
            flag = 1;
            c1.signal();//通知AA线程(精准通知)
        }finally {
            //释放锁
            lock.unlock();
        }
    }
}
/**
 *
 * @Description:
 * 多线程之间按顺序调用,实现A->B->C
 * 三个线程启动,要求如下:
 *
 * AA打印5次,BB打印10次,CC打印15次
 * 接着
 * AA打印5次,BB打印10次,CC打印15次
 * ......来10轮
 *
 * 在线程定制化通信中,使用Lock相比使用syns可以做到精准定位/精准打击.
 * 多线程编程模板(套路)-----上
 * 1 在高内聚低耦合的场景下:线程    操作    资源类
 * 2 判断/干活/通知
 * 3 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断只用while,不能用if)
 * 4 标志位
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 1; i <= 10 ; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 1; i <= 10 ; i++) {
                try {
                    shareResource.print10(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 1; i <= 10 ; i++) {
                try {
                    shareResource.print15(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
    }
}
相关文章
|
7天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
8天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
22 1
|
13天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
40 4
|
19天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
23天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
17 3
|
23天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
16 2
|
23天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
30 2
|
23天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
23天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
43 1
C++ 多线程之初识多线程