《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();
    }
}
相关文章
|
3天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
12 1
|
20小时前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
29天前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
39 6
|
2月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
42 1
[Java]线程生命周期与线程通信
|
1月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
39 3
|
1月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
47 4
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
25 2
|
1月前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
66 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
32 3