线程安全,与多线程的应用

news2024/10/7 8:31:38

一、线程安全

1.什么是线程安全

2.用程序模拟线程安全问题

public class Account {
    private double money;//余额
    private String cardId;//卡号

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }
    public void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
        if (this.money>=money){
            System.out.println (name+"开始取钱");
            this.money-=money;
            System.out.println (name+"取钱成功后余额为"+this.money);
        }else {
            System.out.println ("未取钱余额不足");
        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}




public class MyThread extends Thread{
    private  Account acc;
    public MyThread(Account acc,String name){
        super ( name );
        this.acc=acc;
    }



    @Override
    public void run() {
//        if (acc.getMoney ()>=100000.0){
//            System.out.println (Thread.currentThread ().getName ()+"开始取钱");
//            acc.setMoney (acc.getMoney ()-100000.0);
//            System.out.println (Thread.currentThread ().getName ()+"取钱成功");
//        System.out.println ( Thread.currentThread ().getName ()+"取钱后账户余额"+acc.getMoney () );
//        }else {
//            System.out.println ("账户余额不足");
//        }

        acc.drawMoney ( 100000.0 );
    }

}



public class ThreadTest {
    public static void main(String[] args) {
        //1.创建一个账户类对象代表两个人的共享账户
        Account acc = new Account ( 100000.0, "787878787877" );
        //2.创建两个线程,分别表示小明,小红,两人同时去一个账户中取钱10w
        Thread m1 = new MyThread ( acc,"小明" );
        Thread m2 = new MyThread ( acc,"小红" );
        m1.start ();
        m2.start ();

    }
}

 3.如何解决线程安全问题?

1.线程同步

解决方案:加锁

2.如何实现线程同步(加锁)

1.同步代码块

public class Account {
    private double money;//余额
    private String cardId;//卡号

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }
    public void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
        synchronized (this) {//同步代码块上锁  推荐用共享资源作为锁(this)
            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }
        }
    }
    public static void test(){ //静态方法推荐用类名上锁
        synchronized (Account.class){

        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}
2.同步方法

  public synchronized void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够

            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }

    }

3.Lock锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
    private double money;//余额
    private  String cardId;//卡号
    //创建一个锁对象
    private final Lock lk=new ReentrantLock ();//final  保护锁对象

    public Account() {
    }

    public Account(double money, String cardId) {
        this.money = money;
        this.cardId = cardId;
    }

    public  void drawMoney(double money){
        //先搞清楚是谁来取钱
        String name = Thread.currentThread ().getName ();
        //1.判断账户金额是否足够
            lk.lock ();//加锁
        try {
            if (this.money>=money){
                System.out.println (name+"开始取钱");
                this.money-=money;
                System.out.println (name+"取钱成功后余额为"+this.money);
            }else {
                System.out.println (name+"未取钱余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace ();
        } finally {
            lk.unlock ();//解锁
        }
        

    }
    public static void test(){ //静态方法推荐用类名上锁
        synchronized (Account.class){

        }
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
}

二、线程通信(前提保证线程安全)

1.什么是线程通信

2.代码实例

public class ThreadTest {
    public static void main(String[] args) {
        Desk desk=new Desk ();

        //创建3个生产者线程
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }

        },"厨师1" ).start ();
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }
        },"厨师2" ).start ();
        new Thread ( ()-> {
            while (true) {
                desk.put();
            }

        },"厨师3" ).start ();

        //创建两个消费者线程
       new  Thread(()->{
           while (true) {
               desk.get();
           }
       },"吃货1").start ();
        new  Thread(()->{
            while (true) {
                desk.get();
            }
        },"吃货2").start ();
    }
    }



import java.util.ArrayList;
import java.util.List;

public class Desk {
    private List<String> list =new ArrayList<> ();
    //放包子
    //厨师1,厨师2,厨师3
    public synchronized void put() {

        try {
            String name = Thread.currentThread ().getName ();
            //判断是否由包子
            if(list.size ()==0){
                list.add ( name+"做的肉包子" );
                System.out.println (name+"做了个肉包子");
                Thread.sleep ( 2000 );
             //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }else {
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }
        } catch (Exception e) {
            e.printStackTrace ();
        }

    }
    //取包子
    //吃货1,吃货2
    public synchronized void get() {
        try {
            String name = Thread.currentThread ().getName ();
            if (list.size ()==1){
                //有包子,吃掉
                System.out.println (name+"吃了"+list.get ( 0 ));
                list.clear ();
                Thread.sleep ( 1000 );
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }else {
                //没有包子
                //唤醒别人,等待自己,(用锁对象调用)
                this.notifyAll ();//先唤醒 后等待
                this.wait ();
            }
        } catch (Exception e) {
            e.printStackTrace ();
        }

    }
}


三、线程池

1.什么是线程池

2.线程池的工作原理

控制线程的数量,也控制任务的数量,避免系统创建太多线程,耗费大量的系统资源。挺高系统性能。

3.如何创建线程池

1.方式1(ThreadPoolExecutor对象创建)

import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) {
//            public ThreadPoolExecutor(int corePoolSize,
//                                      int maximumPoolSize,
//                                      long keepAliveTime,
//                                      TimeUnit unit,
//                                      BlockingQueue<Runnable> workQueue) {
//                                      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
//                                      Executors.defaultThreadFactory(), defaultHandler);
        //1.通过ThreadPoolExecutor 创建一个线程池对象
    ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
                new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        }




    }



2.方式2(Executors创建)

import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) throws Exception {
//        //1.通过ThreadPoolExecutor 创建一个线程池对象
//        ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
//                new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        //1-2 通过Executors创建一个线程对象。
        ExecutorService pool = Executors.newFixedThreadPool ( 3 );//新建固定数量线程的线程池
        //计算密集型任务:核心线程数量=CPU核数+1;
        //IO密集型任务:核心线程数量=CPU核数*2;
        Executors.newSingleThreadExecutor ();//新建单个线程数量的线程池
        //2.使用线程处理Callable 任务类
        Future<String> f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
        Future<String> f2 = pool.submit ( new MyCallable ( 200 ) );
        Future<String> f3 = pool.submit ( new MyCallable ( 300 ) );
        Future<String> f4 = pool.submit ( new MyCallable ( 400 ) );

        System.out.println ( f1.get () );
        System.out.println ( f2.get () );
        System.out.println ( f3.get () );
        System.out.println ( f4.get () );


    }
}

3.注意事项

4.线程池处理Runnable任务

1.常用方法

2.新任务的拒绝策略

3.代码

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //描述任务
        System.out.println (Thread.currentThread ().getName ()+"====>666");
        try {
            Thread.sleep ( Integer.MAX_VALUE );
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
    }
}


import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) {
//            public ThreadPoolExecutor(int corePoolSize,
//                                      int maximumPoolSize,
//                                      long keepAliveTime,
//                                      TimeUnit unit,
//                                      BlockingQueue<Runnable> workQueue) {
//                                      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
//                                      Executors.defaultThreadFactory(), defaultHandler);
        //1.通过ThreadPoolExecutor 创建一个线程池对象
    ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
            new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        Runnable target = new MyRunnable ();
        pool.execute ( target );//把任务交给线程池,线程池会自动创建一个新的线程,自动处理这个任务,自动执行。
        pool.execute ( target );
        pool.execute ( target );
        //临时线程的创建时机
        pool.execute ( target );//复用前面的核心线程执行任务
        pool.execute ( target );
        //开始进入任务队列
        pool.execute ( target );
        pool.execute ( target );
        pool.execute ( target );
        pool.execute ( target );
        //开始拒绝策略(任务队列占满)
        pool.execute ( target );//拒绝直接抛出错误
//        pool.shutdown ();//等线程池的任务全部执行完毕后,才会关闭线程池
//        pool.shutdownNow ();//立刻关闭线程池,不管任务是否执行完毕


    }




    }




5.线程池处理Callable任务

1.常用方法

 2.代码

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable() {
    }

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum=0;
        for (int i = 0; i <=n; i++) {
            sum+=i;
        }
        return Thread.currentThread ().getName ()+"求出了1-"+n+"的和是:"+sum;
    }
}



import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) throws Exception {
        //1.通过ThreadPoolExecutor 创建一个线程池对象
        ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
        //2.使用线程处理Callable 任务类
        Future<String> f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
        Future<String> f2 = pool.submit ( new MyCallable ( 200 ) );
        Future<String> f3 = pool.submit ( new MyCallable ( 300 ) );
        Future<String> f4 = pool.submit ( new MyCallable ( 400 ) );

        System.out.println ( f1.get () );
        System.out.println ( f2.get () );
        System.out.println ( f3.get () );
        System.out.println ( f4.get () );


    }
}

四、并发、并行

1.进程和线程的关系

2.什么是并发?

3.什么是并行?

4.多线程是怎么执行的?

五、线程的生命周期

       人的生命周期

sleep(不会释放锁)   wait(会释放锁)

六、悲观锁和乐观锁

1.悲观锁:

一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
public class MyRunnable implements Runnable{
    private int count;
    @Override
    public void run() {
        for (int i = 0; i <100; i++) {

           //悲观锁
            synchronized (this) {
                System.out.println ("count=====>"+ (++count));
            }


        }

    }
}


public class Test1 {
    public static int Number;
    public static void main(String[] args) {
        //悲观锁,乐观锁的原理
        //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
        //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!

        //需求:1个变量,100个线程,每个线程对其加100次
        Runnable target =new MyRunnable ();
        for (int i = 1; i <=100; i++) {
            new Thread (target).start ();
        }


    }
}

2.乐观锁

利用原子类实现

乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
public class MyRunnable2 implements Runnable{
    //乐观锁 CAS算法 比较(Compare)A(and)S(修改Set) 算法
    //整数修改的乐观锁:原子类实现的
    private AtomicInteger count=new AtomicInteger ();

    @Override
    public void run() {
        for (int i = 0; i <100; i++) {


                System.out.println ("count=====>"+ count.incrementAndGet ());//先加1然后再返回值


        }


public class Test2 {

    public static void main(String[] args) {
        //悲观锁,乐观锁的原理
        //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
        //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!

        //需求:1个变量,100个线程,每个线程对其加100次
        Runnable target =new MyRunnable2 ();
        for (int i = 1; i <=100; i++) {
            new Thread (target).start ();
        }


    }
}

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

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

相关文章

LeetCode 1993. 树上的操作:大模拟

【LetMeFly】1993.树上的操作&#xff1a;大模拟 力扣题目链接&#xff1a;https://leetcode.cn/problems/operations-on-tree/ 给你一棵 n 个节点的树&#xff0c;编号从 0 到 n - 1 &#xff0c;以父节点数组 parent 的形式给出&#xff0c;其中 parent[i] 是第 i 个节点的…

基于微信小程序的健康评估系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信端的主要功能有&#xff1a;医生微信端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取…

iOS17正式版BUG汇总:无法正常拨打电话、小组件不可用、无线充电不可用等问题

今天凌晨 iOS 17 正式版发布&#xff0c;相信不少尝鲜派已经更新体验了iOS17的新功能了&#xff0c;但还有很多用户选择观望看是否要升级&#xff0c;小编汇总了目前已更新的用户反馈的已知BUG&#xff0c;供大家查看是否要更新iOS17正式版&#xff01; 目前已知BUG&#xff1…

【LeetCode75】第六十二题 多米诺和托米诺平铺

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我一个数字n&#xff0c;表示我们有2*n大小的地板需要铺。 我们拥有两种瓷砖&#xff0c;一种的长度为2的多米诺&#xff0c;另一…

Jetpack Compose干货,如何让Compose Dialog从屏幕任意方向进入

一、前言 来个效果图&#xff0c;基于Compose Dialog&#xff0c;最终要实现的库能力如下&#xff1a; 这里使用的是这个包下面的&#xff1a; androidx.compose.ui.window.Dialog androidx.compose.material3.AlertDialog它内部调用的也是androidx.compose.ui.window.Dialog …

Centos7 安装部署 Kubernetes(k8s) 高可用集群

1&#xff1a;基础环境准备 宿主机系统集群角色服务器IP主机名称容器centos7.6master192.168.2.150ks-m1dockercentos7.6master192.168.2.151ks-n1dockercentos7.6master192.168.2.152ks-n2docker 1.1 服务器初始化及网络配置 VMware安装Centos7并初始化网络使外部可以访问*…

No2.详解【2023年全国大学生数学建模竞赛】C题——蔬菜类商品的自动定价与补货决策(代码 + 详细输出 + 数据集代码 下载)

只有不回避痛苦和迷茫的人,才有资格去谈乐观和坚定。命运不会厚待谁,悲喜也不会单为你准备。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能…

爬虫获取接口数据

上一讲讲的是获取静态网页数据的教程&#xff0c;适用于我们要爬取的数据在网页源代码中出现&#xff0c;但是还是有很多的数据是源代码中没有的&#xff0c;需要通过接口访问服务器来获得&#xff0c;下面我就来讲讲如何爬取这类数据。 以巨潮资讯网爬取比亚迪企业年报为例。…

解决windows端口占用

WINR打开cmd窗口&#xff1b;输入命令查看哪个进程占用&#xff0c;8848为要查询占用的端口号&#xff1a;netstat -ano | findstr 8848&#xff1b; 3.杀死进程&#xff0c;输入taskkill /f /t /im 10672 其中10672为上面命令查出来的进程号。

24. 图论 - 图的表示种类

Hi&#xff0c;你好。我是茶桁。 之前的一节课中&#xff0c;我们了解了图的来由和构成&#xff0c;简单的理解了一下图的一些相关概念。那么这节课&#xff0c;我们要了解一下图的表示&#xff0c;种类。相应的&#xff0c;我们中间需要穿插一些新的知识点用于更好的去理解图…

Python异步编程并发执行爬虫任务,用回调函数解析响应

一、问题&#xff1a;当发送API请求&#xff0c;读写数据库任务较重时&#xff0c;程序运行效率急剧下降。 异步技术是Python编程中对提升性能非常重要的一项技术。在实际应用&#xff0c;经常面临对外发送网络请求&#xff0c;调用外部接口&#xff0c;或者不断更新数据库或文…

漏刻有时数据可视化Echarts组件开发(31):geomap伪3D配置示例

echarts.registerMap("丹东", getData());let data = getData().features.map((item) => {return {name: item.properties.name,};});const points = [[116.289929,40.265374],[116.754101,40.063877],[116.229504,39.764735],[115.883434,39.899721]]let option …

Unity的AB包相关

1、打包 在这个界面左边右键&#xff0c;CreateNewBundle 将要打包的模型制作成预设体 在下面勾选 选好平台路径&#xff0c;点击Build 2、加载AB包 public class ABTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){//加载AB包AssetB…

pymysql调用存储过程

视频版教程 Python操作Mysql数据库之pymysql模块技术 我们首先创建一个简单的存储过程 DELIMITER //CREATE PROCEDURE test_add(m INT,n INT, OUT result INT) BEGIN SET resultmn;END; //测试&#xff1a; SET s0; CALL test_add(1,2,s); SELECT sPymysql调用存储过程实现&…

从植隆业务中台到金蝶云星空通过接口配置打通数据

从植隆业务中台到金蝶云星空通过接口配置打通数据 数据源系统:植隆业务中台 核心能力以数字化形式沉淀为各种服务中心&#xff0c;其目的是“提供企业能够快速&#xff0c;低成本创新的能力”。业务中台的核心是“构建企业共享服务中心”&#xff0c;其过程是通过业务板块之间的…

代码随想录算法训练营第57天| 647. 回文子串,516.最长回文子序列,动态规划总结

链接: 647. 回文子串 链接: 516.最长回文子序列 链接: 动态规划总结 647. 回文子串 理解dp数组的含义很重 class Solution {public int countSubstrings(String s) {char[] chars s.toCharArray();boolean[][] dp new boolean[s.length()][s.length()];int res 0;// 遍…

FL Studio21水果编曲软件怎么下载中文版?

FL Studio21这款软件在国内被广泛使用&#xff0c;因此又被称为"水果"。它提供音符编辑器&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外&#xff0c;它还提供了方便快捷…

以小见大,彻底理解 cookie,session,token 之间的关系,通俗易懂

发展史 1、很久很久以前&#xff0c;Web 基本上就是文档的浏览而已&#xff0c;既然是浏览&#xff0c;作为服务器&#xff0c; 不需要记录谁在某一段时间里都浏览了什么文档&#xff0c;每次请求都是一个新的 HTTP 协议&#xff0c;就是请求加响应&#xff0c;尤其是我不用记…

菜单栏图标管理软件Bartender mac 5.0.10中文版介绍

Bartender mac是一款菜单栏图标管理软件&#xff0c;功能强大&#xff0c;可以快速管理菜单栏的图标、显示内容和时间&#xff0c;只需在菜单栏中滑动或滚动、单击菜单栏&#xff0c;或者如果您愿意&#xff0c;只需将鼠标悬停即可立即访问隐藏的菜单栏项目。 Bartender软件介绍…

识别准确率达 95%,华能东方电厂财务机器人实践探索

摘 要&#xff1a;基于华能集团公司大数据与人工智能构想理念&#xff0c;结合东方电厂实际工作需要&#xff0c;财务工作要向数字化、智能化纵深推进&#xff0c;随着财务数字化转型和升级加速&#xff0c;信息化水平不断提升&#xff0c;以及内部信息互联互通不断加深&#x…