Java学习之线程2

多线程中对变量的互斥实:

1. 对多线程模式中共享变量的上锁

public class DemoSynchronized { 
    
    public static void main(String[] args) {
        /*
            TODO :
                多线程模式中出现的问题:
                    1.对于总的销售票数 会大于100张
                    2.对于票数可能会出现负数 不符合代码的执行逻辑
                总结:对于多个线程使用同一个变量,那么会造成线程不安全的问题
                        如何解决? => 线程同步 => 当使用一个变量时,会对当前变量进行加锁,不让其他线程获取到变量
                                                     但是其他线程可以获取到CPU的执行权
                解决思路:
                    对于共享变量的使用,需要对其进行加锁操作
                    格式:
                        synchronized(对象){需要同步的代码;}
                    注意:
                        ① 如果当 synchronized 将 sell 方法进行对当前对象加锁,那么可能会出现售票为0的现象
                            原因:while循环中的ticksNum可能会被其他的线程所抢占CPU执行导致当售票为0时能进入循环
                        ② 如果当 synchronized 将while循环进行对当前对象枷锁,那么对导致只有一个线程运行
                            原因: 当一个线程获取到 sellRunnable对象之后对其进行加锁
                                        加锁以后没有释放,所以会导致一个线程会一直拥有对象,其他线程没有办法获取到
                                            对象,就没有办法执行while循环
                            如何释放锁?
                                对于synchronized修饰的代码块,当执行完成后,那么会对其进行释放锁

         */
        //TODO synchronized对对象进行枷锁
        // 情况一: sellRunnable对象是 全局唯一的 ,并且其中的变量ticksNum也是唯一的;对当前对象进行上锁
        SellRunnable sellRunnable = new SellRunnable();
        new Thread(sellRunnable, "售票员1").start();
        new Thread(sellRunnable, "售票员2").start();

        // 情况二: 当每个线程拥有自己独有的对象,每个线程只对当前自己的对象加锁而不影响其它线程
        SellRunnable sellRunnable1 = new SellRunnable();
        SellRunnable sellRunnable2 = new SellRunnable();
        new Thread(sellRunnable1, "售票员1").start();
        new Thread(sellRunnable2, "售票员2").start();


        //情况三: 使用一个类来作为唯一、共享的资源;可以实现对资源的互斥访问
        TicksNum ticksNum = new TicksNum();
        new SynchronizedThread("售票员1",ticksNum).start();
        new SynchronizedThread("售票员2",ticksNum).start();

        //情况四: 不可以同步:传入的共享资源必须唯一
        TicksNum ticksNum1 = new TicksNum();
        TicksNum ticksNum2 = new TicksNum();
        new SynchronizedThread("售票员1",ticksNum1).start();
        new SynchronizedThread("售票员2",ticksNum2).start();
    }


    static class SellRunnable implements Runnable {
        int ticksNum = 100;

        @Override
        public void run() {
            // TODO 一旦当线程获取到唯一的一个this对象,其他线程如果想要执行同步代码块,必须要等当前线程释放

            // 同步代码块
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //synchronized对当前SellRunnable的对象进行上锁
                synchronized (this) {
                    // TODO this 代表当前 SellRunnable 的对象 => 在main方法中只有一个sellRunnable对象
                    if (ticksNum > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }

        // 销售的方法
        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + ticksNum + "票");
            ticksNum -= 1;
        }
    }

    static class SynchronizedThread extends Thread {
        TicksNum ticksNum;  // TODO 对于ticksNum可以是唯一的一个对象

        public SynchronizedThread(String name, TicksNum ticksNum) {
            //设置进程名
            super(name);
            //设定共享资源
            this.ticksNum = ticksNum;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO 如果要加锁,那么需要寻找唯一存在的东西  可以使用传入的 ticksNum  => 但是要求只能传入一个
                //   对唯一的资源进行加锁
                synchronized (ticksNum) {
                    // TODO this 代表的是 SynchronizedThread 的对象
                    if (ticksNum.getNum() > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }

        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + ticksNum.getNum() + "票");
            ticksNum.sellTick();
        }
    }

    static class TicksNum {
        int num = 100;

        public void sellTick() {
            num -= 1;
        }

        public int getNum() {
            return num;
        }
    }

}

2. 锁定类来实现同步1

public class DemoSynchronized1Clazz {
    
    public static void main(String[] args) {
        /*
           TODO
                由于对于同步代码块来说,如果给定的锁是一个对象,那么该对象可能在调用方调用时
                    给定多个对象,那么就失去了同步的意义  于是寻找唯一的一个类别 => 类
                总结:
                    可以在同步代码块中使用特定的类来锁定代码,但是当前的写法,使用的是静态变量
                        在有些场合下,不是很方便

         */
        new SynchronizedClazz("销售员1").start();
        new SynchronizedClazz("销售员2").start();

    }


    static class SynchronizedClazz extends Thread{
        public SynchronizedClazz(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                // TODO 针对TicksNumClazz使用加锁操作 表示在同一时刻 有且只能使用一次该
                //       对一个类进行上锁,对于静态内部类有如下写法
                synchronized (TicksNumClazz.class) {
                    if (TicksNumClazz.getNum() > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }

        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + TicksNumClazz.getNum() + "票");
            //调用静态内部类中的方法实现票数的减一操作
            TicksNumClazz.sellTick();
        }
    }

    static class TicksNumClazz {
        static int num = 100;
        public static void sellTick() {
            num -= 1;
        }

        public static int getNum() {
            return num;
        }
    }
}

3. 锁定类来实现同步2

public class DemoSynchronized2Clazz {
    
    public static void main(String[] args) {
        /*
           TODO
                由于对于同步代码块来说,如果给定的锁是一个对象,那么该对象可能在调用方调用时
                    给定多个对象,那么就失去了同步的意义  于是寻找唯一的一个类别 => 类
                总结:
                    可以在同步代码块中使用特定的类来锁定代码,但是当前的写法,使用的是静态变量
                        在有些场合下,不是很方便
                    解决方案如下:

         */
        //传入的 ticksNum 变量唯一,故此可以实现同步操作
        TicksNum ticksNum = new TicksNum();
        TicksNumObject ticksNumObject1 = new TicksNumObject(ticksNum);
        TicksNumObject ticksNumObject2 = new TicksNumObject(ticksNum);
        new SynchronizedClazz("销售员1",ticksNumObject1).start();
        new SynchronizedClazz("销售员2",ticksNumObject2).start();

    }

    static class SynchronizedClazz extends Thread{
        TicksNumObject ticksNumObject;
        public SynchronizedClazz(String name,TicksNumObject ticksNumObject) {
            super(name);
            this.ticksNumObject = ticksNumObject;
        }

        @Override
        public void run() {
            while (true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO 针对TicksNumObject使用加锁操作 表示在同一时刻 有且只能使用一次该类
                synchronized (TicksNumObject.class) {
                    if (ticksNumObject.ticksNum.getNum() > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }
        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + ticksNumObject.ticksNum.getNum() + "票");
            ticksNumObject.ticksNum.sellTick();
        }
    }


    static class TicksNumObject {
        //TODO 更改其类内部的变量、方法,使其不是静态的;并将原类中的变量num通过一个类来进行实现
        TicksNum ticksNum;
        public TicksNumObject(TicksNum ticksNum) {
            this.ticksNum = ticksNum;
        }
    }

    static class TicksNum{
        int num = 100;
        public void sellTick() {
            num -= 1;
        }
        public int getNum() {
            return num;
        }
    }

}

4. notify()、wait()的实现

public class DemoThreadConnection {
    
    public static void main(String[] args) {
        /*
           TODO:
                由于同步代码块中,是对当前代码进行加锁操作,线程如果要运行,必须要获取全局唯一的一把锁
                     那么从运行结果看,线程之间执行是没有顺序的,同一个线程可能被执行多次
                那么如果想要实现多个线程之间相互执行,如何操作,并且同步代码块是以代码块加锁,能否使用线程阻塞方式
                    如果一个线程执行到某个条件时,可以通过调用方法对当前线程进行阻塞,同时唤醒其他被阻塞的线程

           TODO 注意:
                ① synchronized 也可以对当前 成员方法 进行加锁,加锁的是当前对象
                ② synchronized 也可以对当前 静态方法 进行加锁,加锁的是当前方法所属的类  => 实现 ???
                ③ notify 作用是从因为当前对象被锁定的线程中唤醒一个线程
                ④ wait 作用是从当前对象中的当前线程进行等待阻塞,如果要被唤醒,那么必须有一个线程使用 notify 来进行
                ⑤ notify和wait必须在synchronized修饰的代码块或者方法中才能存在
                ⑥ notify和wait是属于顶级父类Object中的方法
                ⑦ notify和wait 调用的对象,必须是被加锁的
                ⑧ 当代码中先wait之后再notify代码无法完整执行,暂停了..
                    原因:

         */

        TickRunnable tickRunnable = new TickRunnable(100);
        new Thread(tickRunnable,"销售员1").start();
        new Thread(tickRunnable,"销售员2").start();

        TickRunError tickRunError = new TickRunError(new TicksNum());
        new Thread(tickRunError, "销售员1").start();
        new Thread(tickRunError, "销售员2").start();
    }

    static class TickRunError implements Runnable {
        TicksNum tickNum;

        public TickRunError(TicksNum tickNum) {
            this.tickNum = tickNum;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO 对tickNum对象进行上锁
                synchronized (tickNum) {
                    if (tickNum.getNum() > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }

        //TODO 通过notify、wait方法实现了两个售票员的以此售票
        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + tickNum.getNum()+ "票");
            tickNum.sellTick();
            try {
                tickNum.notify(); // 唤醒其他线程
//                tickNum.notifyAll(); // 唤醒所有线程
                tickNum.wait();  // 对当前的对象进行阻塞

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    static class TicksNum {
        static int num = 100;

        public void sellTick() {
            num -= 1;
        }

        public int getNum() {
            return num;
        }
    }


    static class TickRunnable implements Runnable {
        int tickNum;

        public TickRunnable(int tickNum) {
            this.tickNum = tickNum;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO 针对TicksNumObject使用加锁操作 表示在同一时刻 有且只能使用一次该类
                if (tickNum > 0) {
                    sell();
                } else {
                    break;
                }
            }
        }

        //TODO synchronized 对当前 成员方法 进行加锁,
        // 加锁的是当前对象;tickRunnable对象是全局唯一的,故此可以实现同步操作
        public synchronized void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + tickNum + "票");
            tickNum -= 1;
            try {
//                this.notify(); // 唤醒其他线程
                this.notifyAll(); // 唤醒所有线程
                this.wait();  // 对当前的对象进行阻塞
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


}

5. lock()的实现

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

public class DemoLockAndUnlock {
    
    public static void main(String[] args) {
        TickLock tickLock = new TickLock(100, new ReentrantLock());
        new Thread(tickLock,"销售员1").start();
        new Thread(tickLock,"销售员2").start();

    }

    static class TickLock implements Runnable {
        int tickNum;
        Lock lock;

        public TickLock(int tickNum, Lock lock) {
            this.tickNum = tickNum;
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                // TODO 相当于在当前位置添加了一个 synchronized 同步锁
                lock.lock();
                if (tickNum > 0) {
                    sell();
                } else {
                    break;
                }
                lock.unlock();
            }
        }

        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + tickNum + "票");
            tickNum -= 1;
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

启明云端ESP8266+企业微信考勤机项目,多种方式认证能防止代打

智能考勤机需要有识别功能,用户容量,记录容量限制,还有物联网通讯方式,最后衔接到云平台,最后就是根据具体需求来设计。 ①识别方式:现如今市场上的考勤机主要有人脸、指纹、IC卡和ID卡等多种识别方式。不…

虚拟机文件夹共享操作(本地访问)

新建一个文件夹 右击文件夹点击属性 找到共享 点击共享 选择本地用户共享就可以了 本地winr 输入我们图片中的格式(IP前加 “\\” ) 会弹一个窗口,输入虚拟机的入户名和密码就可以共享了(一般默认用户名都是administrator&am…

人工智能-2024期中考试

前言 人工智能期中考试,认真准备了但是没考好,结果中游偏下水平。 第4题没拿分 (遗传算法:知识点在课堂上一笔带过没有细讲,轮盘赌算法在书本上没有提到,考试的时候也没讲清楚,只能靠猜&…

Linux进程——Linux进程与进程优先级

前言:在上一篇了解完一部分常见的进程状态后,我们先来把剩下的进程状态了解一下,再来进入进程优先级的学习! 如果对前面Linux进程不太熟悉可以先阅读: Linux进程 本篇主要内容: 僵尸进程和孤儿进程 Linux进…

绘画作品3d数字云展厅提升大众的艺术鉴赏和欣赏能力

3D虚拟展厅作为未来艺术的展示途径,正逐渐成为文化创意产业蓬勃发展的重要引擎。这一创新形式不仅打破了传统艺术展览的局限性,更以其独特的魅力吸引着全球观众的目光。 3D虚拟艺术品展厅以其独特的魅力,助力提升大众的艺术鉴赏和欣赏能力。观…

python - rst file to html

文章目录 python - rst file to html概述笔记下载安装PyCharm最新的学习版新建虚拟环境为Conda的工程添加docutils库新建python文件,添加转换代码运行自己写的python文件,执行转换转换结果END python - rst file to html 概述 开源工程中有一个.rst文件…

自动驾驶主流芯片及平台架构(一)

零部件成本下降、中低端车竞争加剧,推动ADAS渗透率在中国市场快速提升,自主品牌ADAS装配量大幅提升 零部件成本下降、中低端车竞争加剧,推动ADAS渗透率在中国市场快速提升,自主品牌ADAS装配量大幅提升。5年前在一些高端车型上才有…

(持续更新升级)火爆的ChatGPT源码+高质量AI绘画系统+分销功能+详细图文搭建部署教程

随着人工智能技术的迅猛发展,智能对话和创意艺术不再是遥不可及,而是可以触手可及的现实。 分享一款集ChatGPT源码、高质量AI绘画系统以及强大分销功能于一体的系统源码,对接了大名鼎鼎的ChatGPT接口及Midjourney两个王牌接口,另…

C++ 函数与指针

函数内部数据是地址需要传递给调用函数,返回的当然是指针了!当然,这个返回地址也可以通过函数参数返回! 函数的参数是指针可以输出函数多个结果,返回值本身就是返回数据,什么时候需要返回指针呢&#xff1f…

【北京迅为】《iTOP-3588开发板源码编译手册》-第4章 Android12/Linux设备树简介

RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

迅为RK3568开发板资料说明4750+页专属文档专为3568编写

iTOP-3568开发板采用瑞芯微RK3568处理器,内部集成了四核64位Cortex-A55处理器。主频高达2.0Ghz,RK809动态调频。集成了双核心架构GPU,ARM G52 2EE、支持OpenGLES1.1/2.0/3.2、OpenCL2.0、Vulkan1.1、内嵌高性能2D加速硬件。 内置独立NPU,算力…

antdVue 自定义table列配置

最近做项目的时候需要对页面的table进行列配置的需求 子组件 <div><a-modaltitle"列配置" :visible"visible" :closable"false" :footer"null"width"800px" height"448px"><div><a-row>…

Netty一文搞懂入门篇<随手笔记>

1.Java IO的读写原理 IO是Input和Output的缩写&#xff0c;即输入和输出。用户程序进行IO的读写基本上会用到read和write两大系统调用。 read把数据从内核缓冲区复制到进程缓冲区&#xff0c;write是把数据从进程缓冲区复制到内核缓冲区。 这两大系统的调用都不负责数据在内核…

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

作者&#xff1a;天彤 Atlassian 在 2020 年官方发布公告&#xff0c;从 2021 年起停止 Jira Server 产品的销售&#xff0c;并且在 2024 年彻底停止 Server 端产品的服务支持&#xff0c;这对于国内使用 Jira 产品的企业和研发团队造成了不小的影响。而此时国内很多 DevOps 产…

LeetCode面试298,二叉树最长连续序列(Python)

开始想着dfs&#xff0c;两种情况 1.以root为根 2.不以root为根 但是这样需要两个dfs分别进行&#xff0c;那么时间复杂度就上去了。 class Solution:def longestConsecutive(self, root: Optional[TreeNode]) -> int:def dfs(root):# 以root为根节点&#xff0c;可以延…

【系统分析师】系统分析部分

文章目录 1、系统分析概述2、详细调查2.1 为什么要做详细调查&#xff1f;2.2 详细调查的原则2.3 详细调查的内容2.4 详细调查的方法 3、现有系统分析3.1 获得系统的物理模型3.2 抽象出现有系统的逻辑模型3.3 建立新系统的逻辑模型3.4 建立新系统的物理模型 4、组织结构分析4.1…

文件夹批量重命名,轻松实现简体中文翻译成繁体中文,文件夹批量改名新体验

文件夹的管理和命名显得尤为重要。你是否曾为了给文件夹取一个合适的名字而 绞尽脑汁&#xff1f;是否因为需要批量修改文件夹名而苦恼不已&#xff1f;现在&#xff0c;我们为你带来一款强大的文件夹批量改名工具&#xff0c;不仅能轻松实现简体中文到繁体中文的转换&#xf…

5月7日监控二叉树+斐波那契数

968.监控二叉树 给定一个二叉树&#xff0c;我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 示例 1&#xff1a; 输入&#xff1a;[0,0,null,0,0] 输出&#xff1a;1 解释&#xff…

LeCun转发,AI让失语者重新说话!纽约大学发布全新「神经-语音」解码器 | 最新快讯

新智元报道 编辑&#xff1a;LRT 通过采集皮层电图&#xff08;ECoG&#xff09;的数据信号&#xff0c;模型可以将其转换为可解释的语音参数&#xff08;如音高&#xff0c;响度&#xff0c;共振峰频率等&#xff09;&#xff0c;并合成出既准确又自然的语音波形。 脑机接口&a…

【C++ | 函数】默认参数、哑元参数、函数重载、内联函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-05-04 1…
最新文章