《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();
    }
}
相关文章
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
138 0
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
3月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
233 5
|
4月前
|
存储 缓存 安全
JUC并发—11.线程池源码分析
本文主要介绍了线程池的优势和JUC提供的线程池、ThreadPoolExecutor和Excutors创建的线程池、如何设计一个线程池、ThreadPoolExecutor线程池的执行流程、ThreadPoolExecutor的源码分析、如何合理设置线程池参数 + 定制线程池。
JUC并发—11.线程池源码分析
|
7月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
238 20
|
7月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
9月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
158 1
|
11月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
138 1
C++ 多线程之初识多线程
|
11月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
192 3
|
11月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
106 2