《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();
    }
}
相关文章
|
15天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
6天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
7天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
15 1
|
9天前
|
存储 缓存 Java
线程同步的艺术:探索 JAVA 主流锁的奥秘
本文介绍了 Java 中的锁机制,包括悲观锁与乐观锁的并发策略。悲观锁假设多线程环境下数据冲突频繁,访问前先加锁,如 `synchronized` 和 `ReentrantLock`。乐观锁则在访问资源前不加锁,通过版本号或 CAS 机制保证数据一致性,适用于冲突少的场景。锁的获取失败时,线程可以选择阻塞(如自旋锁、适应性自旋锁)或不阻塞(如无锁、偏向锁、轻量级锁、重量级锁)。此外,还讨论了公平锁与非公平锁,以及可重入锁与非可重入锁的特性。最后,提到了共享锁(读锁)和排他锁(写锁)的概念,适用于不同类型的并发访问需求。
38 2
|
10天前
|
Java 程序员 编译器
Java中的线程同步与锁优化策略
【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。
|
16天前
|
Java
Java中的多线程实现:使用Thread类与Runnable接口
【4月更文挑战第8天】本文将详细介绍Java中实现多线程的两种方法:使用Thread类和实现Runnable接口。我们将通过实例代码展示如何创建和管理线程,以及如何处理线程同步问题。最后,我们将比较这两种方法的优缺点,以帮助读者在实际开发中选择合适的多线程实现方式。
22 4
|
18天前
|
安全 Java 调度
深入理解Java中的线程安全与锁机制
【4月更文挑战第6天】 在并发编程领域,Java语言提供了强大的线程支持和同步机制来确保多线程环境下的数据一致性和线程安全性。本文将深入探讨Java中线程安全的概念、常见的线程安全问题以及如何使用不同的锁机制来解决这些问题。我们将从基本的synchronized关键字开始,到显式锁(如ReentrantLock),再到读写锁(ReadWriteLock)的讨论,并结合实例代码来展示它们在实际开发中的应用。通过本文,读者不仅能够理解线程安全的重要性,还能掌握如何有效地在Java中应用各种锁机制以保障程序的稳定运行。
|
22天前
|
安全 Java 容器
Java并发编程:实现高效、线程安全的多线程应用
综上所述,Java并发编程需要注意线程安全、可见性、性能等方面的问题。合理使用线程池、同步机制、并发容器等工具,可以实现高效且线程安全的多线程应用。
14 1
|
26天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
28天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0

热门文章

最新文章