解决线程安全问题,Lock锁,死锁以及如何避免,线程的通信和线程池

news2025/1/15 8:14:45

如何解决线程安全问题

当多个线程共享一个资源时,则可能出现线程安全问题。 java中解决线程安全的方式有三种

第一种: 同步代码块

第二种: 同步方法

第三种: Lock

同步代码块

synchronized(锁对象){
    需要同步的代码。
}

  1. synchronized 同步的意思

  2. 锁对象可以是任何引用类型的java对象,只要是一个java对象就可以当做锁对象

  3. 锁对象一旦被确定下来要保证唯一性, 锁对象只能有一份

public class SellTicket implements Runnable {
    private int tickets = 100;//票数
    private StringBuffer sb = new StringBuffer();

    @Override
    public void run() {
        while (tickets > 0) {
            synchronized (this) { //this:表示当前类对象。因为外界就创建了一个该类对象
//            synchronized (sb) { //sb表示StringBuffer对象,StringBuffer可变。
//            synchronized (SellTicket.class) { //获取当前类的反射类 也可以
                if (tickets>0) { //判断票数是否大于0
                    System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + --tickets + "张");
                }
            }
            //            }
        }
    }
}

 同步方法

修饰符 synchronized 返回值类型 方法名 ( 方法参数 ) {
        方法体;
}

同步方法默认的锁对象就是:this

 什么时候使用同步成员方法?

如果你发现你写的一个成员方法 , 这个方法内部所有的代码都使用同步代码块给包裹了 , 并且使用的锁对象是this的时候 . 就可以使用同步成员方法简化这个方法。

public class SellTicket2 implements Runnable {
    private int tickets = 100;//票数
    private StringBuffer sb = new StringBuffer();

    @Override
    public void run() {
        while (tickets > 0) {
               sell();
        }
    }
    //同步方法
    private synchronized void sell(){
        if (tickets>0) { //判断票数是否大于0
            System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + --tickets + "张");
        }
    }
}

 Lock锁

手动锁,需要手动加锁和释放锁。实现类 ReentrantLock . 早期Lock锁效率高于synchronized,但是jdk1.6后进行synchronized优化,二者相差不大,主要看个人喜好

 

public class SellTicket implements Runnable {
    private int tickets = 100;//票数
    private Lock l=new ReentrantLock();//创建锁对象

    @Override
    public void run() {
        while (tickets > 0) {
            try {
                l.lock();//上锁
                if(tickets>0) {
                    System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + --tickets + "张");
                }
            } finally {
                l.unlock();//释放锁--建议释放锁放在finally中
            }
        }
    }
}

 死锁

线程死锁是指由于两个或者多个线程相互持有对方所需要的锁资源,导致这些线程处于等待状态,无法继续执行。

 例子: 画画 【画笔,画板】 线程A拥有画笔 需要画板 线程B拥有画板,需要画笔

 

package org.example.demo09;

public class Test01 {
    private static String a="画笔";
    private static String b="画板";
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                  synchronized (a){
                      System.out.println("线程A拥有画笔");
                      synchronized (b){
                          System.out.println("线程A拥有画板");
                          System.out.println("线程A可以画画");
                      }
                  }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (b){
                    System.out.println("线程B拥有了画板");
                    synchronized (a){
                        System.out.println("线程B拥有了画笔");
                        System.out.println("线程B可以画画");
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

如何避免死锁

1. 使用tryLock设置获取时间。
2. 可以使用juc包下的类。--线程安全类
3. 尽量减少同步代码块的嵌套。

线程的通信

 

package org.example.demo12;

public class BankCard {
    private double balance;//余额
    private boolean flag = false;//表示卡中是否有钱 false表示没钱 true 表示有钱

    //存
    public synchronized void deposit(double amt) {
        if (flag == false) {
            balance += amt;
            System.out.println(Thread.currentThread().getName() + "存款" + amt + "元,余额为:" + balance);
            notify();//唤醒正在等待的线程
        }
        flag = true;
        try {
            wait();//需要进入等待队列
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    //取
    public synchronized void takeMoney(double amt) {
        if (flag == true) {
            balance -= amt;
            System.out.println(Thread.currentThread().getName() + "取款" + amt + "元,余额为:" + balance);
            notify();
        }
        flag = false;
        try {
            wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

 

package org.example.demo12;

public class SaveTask implements Runnable{
    private BankCard card;
    public SaveTask(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.deposit(10000);
        }
    }
}
package org.example.demo12;

public class TakeTask implements Runnable{
    private BankCard card;
    public TakeTask(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.takeMoney(10000);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //创建一个银行卡对象
        BankCard card=new BankCard();
        //创建两个任务对象
        SaveTask task1=new SaveTask(card);
        TakeTask task2=new TakeTask(card);
        //创建两个线程对象
        Thread t1=new Thread(task1,"李晨");
        Thread t2=new Thread(task2,"范冰冰");

        t1.start();
        t2.start();
    }
}

wait和sleep方法的区别。

  1. wait属于Object类中的方法 而sleep属于Thread类的方法。

  2. wait必须放在同步代码块中,sleep可以在任何位置使用。

  3. wait会释放锁资源,而sleep不会释放锁资源。

  4. wait需要通过notify|notifyAll唤醒,而sleep时间到了自动唤醒。

线程的状态

 线程生命周期,线程对象从生到死一个过程,当线程被创建并启动以后,他既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。

 NEW: 新建状态.
RUNNABLE:运行状态。【就绪和运行】
BLOCKED:堵塞状态
WAITING: 等待状态
TIME_WAITING: 有时间的等待
TERMINSTED:终止

 

线程池

什么是线程池

提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子 ( 容器 ) ,在该池子中存储很多个线程。一个装着多条线程对象的容器就是线程池 。

为什么使用线程池

1、提高性能:线程的创建和销毁需要消耗一定的系统资源,使用线程池可以重复利用已经创建好的线程,避免频繁地创建和销毁线程,从而提高程序的性能。

2、提高响应速度:当任务到达时,如果线程池中有空闲的线程,则可以立即执行任务,提高程序的响应速度。

3、控制并发线程数:线程池可以控制同时运行的线程数,避免由于过多的线程导致系统资源占用过多或者系统崩溃等问题。 

创建线程池的核心参数

线程池的工作原理

创建线程池的方式

ThreadPoolExecutor [推荐]

package org.example.demo13;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test02 {
    public static void main(String[] args) {
        /**
         * int corePoolSize,核心线程数
         * int maximumPoolSize,最大线程数
         * long keepAliveTime,非核心线程的存活时间
         * TimeUnit unit 时间单位
         * BlockingQueue<Runnable> workQueue: 等待队列
         */
        BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(5);
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(2,5,20, TimeUnit.SECONDS,workQueue);
        //15 10 10  20  15
        for (int i = 0; i < 11; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行了任务");
                }
            });
        }
    }
}

线程池大小如何设定.

最简回答:生产环境中,Java线程池大小的设定与硬件资源和并发需求密切相关。通常可以考虑CPU核心数、内存容量、网络带宽等硬件资源,并结合预估的并发请求量来确定线程池大小,以充分利用资源并保持合理的并发处理能力。较多的硬件资源和高并发通常需要更大的线程池来提高并发处理效率。

Executors工具【不推荐】

package org.example.demo13;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test01 {
    public static void main(String[] args) {
        //1.采用Executors工具类创建线程池
//        ExecutorService executorService = Executors.newFixedThreadPool(5);//创建一个固定的线程池.
//        ExecutorService executorService = Executors.newSingleThreadExecutor();//创建单一的线程池。保证任务的有序执行。
        ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个变的线程池。
        //ExecutorService executorService = Executors.newCachedThreadPool();//创建一个缓存的线程池。
        //2.通过线程池对象执行任务.submit()可以执行Runnable类型的任务也可以执行Callable类型的任务  execute()方法只能执行Runnable类型的任务.
        for (int i = 0; i < 300000; i++) { 
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行了任务");
                }
            });
        }
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

源码编译安装httpd 2.4,提供系统服务管理脚本并测试(两种方法实现)

方法一&#xff1a;使用 systemd 服务文件 sudo yum install gcc make autoconf apr-devel apr-util-devel pcre-devel 1.下载源码 wget https://archive.apache.org/dist/httpd/httpd-2.4.46.tar.gz 2.解压源码 tar -xzf httpd-2.4.46.tar.gz 如果没有安装tar 记得先安装…

基于微信小程序的智能停车场管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

LabVIEW驱动电机实现样品自动搜索

利用LabVIEW控制电机驱动相机在XY平面上进行扫描&#xff0c;以检测样品位置。样品最初可能位于相机视野范围之外&#xff0c;需要实现自动搜索样品位置并完成精确定位扫描的功能。该系统需具有以下特点&#xff1a; 高效搜索&#xff1a;能够快速确定样品位置&#xff0c;缩短…

excel 整理表格,分割一列变成多列数据

数据准备 对于很多系统页面的数据是没有办法下载的。 这里用表格数据来举例。随便做数据的准备。想要看excel部分的可以把这里跳过&#xff0c;从数据准备完成开始看。 需要一点前端基础知识&#xff0c;但不多&#xff08;不会也行&#xff09;。 把鼠标放在你想要拿到本地的…

MAC AndroidStudio模拟器无网络

先确认PC端是正常访问网络的&#xff1b; 模拟器端修改Wifi设置&#xff1a;设置 - 网络和互联网 - WALN设置 按照上图修改&#xff1b; IP设置&#xff1a;从DHCP修改为静态&#xff0c;IP地址&#xff1a;10.0.2.16 &#xff0c;网关&#xff1a;10.0.2.2 &#xff0c; DNS…

【Linux系统】Ext系列磁盘文件系统一

0. 从快递系统引入文件系统 理解文件系统&#xff1a;菜鸟驿站的类比 在日常生活中&#xff0c;我们常常会使用到快递服务来寄送和接收包裹。这个过程虽然看似简单&#xff0c;但背后却有着一套复杂而有序的管理系统在支撑。今天&#xff0c;我们将通过一个类比——将文件系统…

1Hive概览

1Hive概览 1hive简介2hive架构3hive与Hadoop的关系4hive与传统数据库对比5hive的数据存储 1hive简介 Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张数据库表&#xff0c;并提供类SQL查询功能。 其本质是将SQL转换为MapReduce/Spark的任务进…

Elasticsearch入门学习

Elasticsearch是什么 Elasticsearch 是一个基于 Apache Lucene 构建的分布式搜索和分析引擎、可扩展的数据存储和矢量数据库。 它针对生产规模工作负载的速度和相关性进行了优化。 使用 Elasticsearch 近乎实时地搜索、索引、存储和分析各种形状和大小的数据。 特点 分布式&a…

[读书日志]8051软核处理器设计实战(基于FPGA)第七篇:8051软核处理器的测试(verilog+C)

6. 8051软核处理器的验证和使用 为了充分测试8051的性能&#xff0c;我们需要测试每一条指令。在HELLO文件夹中存放了整个测试的C语言工程文件。主函数存放在指令被分为五大类&#xff0c;和上面一样。 打开后是这样的文件结构。HELLO.c是主文件&#xff0c;这是里面的代码&am…

【Vue实战】Vuex 和 Axios 拦截器设置全局 Loading

目录 1. 效果图 2. 思路分析 2.1 实现思路 2.2 可能存在的问题 2.2.1 并发请求管理 2.2.2 请求快速响应和缓存带来的问题 3. 代码实现 4. 总结 1. 效果图 如下图所示&#xff0c;当路由变化或发起请求时&#xff0c;出现 Loading 等待效果&#xff0c;此时页面不可见。…

一文读懂yolo11模型训练

一文读懂yolo11模型训练 一、环境准备 Anaconda安装 简介 Anaconda 是一个流行的开源 Python 发行版&#xff0c;专注于数据科学、机器学习、科学计算和分析等领域。它提供了一个强大的包管理器和环境管理器&#xff0c;名为 Conda&#xff0c;以及一个预装了大量科学计算和…

Apache PAIMON 学习

参考&#xff1a;Apache PAIMON&#xff1a;实时数据湖技术框架及其实践 数据湖不仅仅是一个存储不同类数据的技术手段&#xff0c;更是提高数据分析效率、支持数据驱动决策、加速AI发展的基础设施。 新一代实时数据湖技术&#xff0c;Apache PAIMON兼容Apache Flink、Spark等…

音视频入门基础:RTP专题(1)——RTP官方文档下载

一、引言 实时传输协议&#xff08;Real-time Transport Protocol&#xff0c;简写RTP&#xff09;是一个网络传输协议&#xff0c;由IETF的多媒体传输工作小组1996年在《RFC 1889》中公布的。 RTP作为因特网标准在《RFC 3550》有详细说明。而《RFC 3551》详细描述了使用最小…

【Vim Masterclass 笔记13】第 7 章:Vim 核心操作之——文本对象与宏操作 + S07L28:Vim 文本对象

文章目录 Section 7&#xff1a;Text Objects and MacrosS07L28 Text Objects1 文本对象的含义2 操作文本对象的基本语法3 操作光标所在的整个单词4 删除光标所在的整个句子5 操作光标所在的整个段落6 删除光标所在的中括号内的文本7 删除光标所在的小括号内的文本8 操作尖括号…

LiveGBS流媒体平台GB/T28181常见问题-没有收到视频流播放时候提示none rtp data receive未收到摄像头推流如何处理?

LiveGBS没有收到视频流播放时候提示none rtp data receive未收到摄像头推流如何处理&#xff1f; 1、none rtp data receive2、搭建GB28181视频直播平台 1、none rtp data receive LiveSMS 收不到下级推流 首先需要排查服务器端 UDP & TCP 30000-30249 端口是否开放其次排…

使用Docker模拟PX4固件的无人机用于辅助地面站开发

前言 最近在制作鸿蒙无人机地面站&#xff0c;模仿的是QGroundControl&#xff0c;协议使用mavlink&#xff0c;记录一下本地模拟mavlink协议通过tcp/udp发送 废话不多说直接上命令 1.启动docker的桌面端 启动之后才能使用docker命令来创建容器 docker run --rm -it jonas…

【Docker】保姆级 docker 容器部署 MySQL 及 Navicat 远程连接

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. docker 容器部署 MySQL1.1 拉取mysql镜像1.2 启动容器1.3 进入容器1.4 使用 root 用户登录 2. Navicat 连…

【大数据】机器学习-----线性模型

一、线性模型基本形式 线性模型旨在通过线性组合输入特征来预测输出。其一般形式为&#xff1a; 其中&#xff1a; x ( x 1 , x 2 , ⋯ , x d ) \mathbf{x}(x_1,x_2,\cdots,x_d) x(x1​,x2​,⋯,xd​) 是输入特征向量&#xff0c;包含 d d d 个特征。 w ( w 1 , w 2 , ⋯ ,…

装备制造行业(复杂机械制造)数字化顶层规划 - 汇报会

行业业务特点&#xff1a; 尊敬的各位管理层&#xff1a; 大家好&#xff01;今天我将向大家汇报装备制造企业数字化战略的顶层规划设计。在当今数字化浪潮下&#xff0c;装备制造企业面临着转型升级的迫切需求&#xff0c;数字化战略的制定与实施对于提升企业竞争力、实现可持…

深度探索C++20协程机制

#include <iostream> #include <coroutine>class CoroTaskSub { public://编译器在处理协程函数时是通过其返回类型【即协程接口类型】&#xff0c;确定协程的承诺类型和协程句柄类型struct promise_type;using CoroHdl std::coroutine_handle<promise_type>…