Java 多线程练习

news2024/9/29 21:18:20

目录

1.定时器操作(实现电脑定时关机)。

2. 每个月的月末(02:00:00) 执行一次代码

3. 模拟售票

4. 用15个线程实现,求123456789 之间放+-和为100的表达式(11个结果),如果一个线程求出结果, 立即告诉其它停止。

 5. 实现一个容器,提供两个方法,add,count 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。(10个线添加元素,1个线程监控统计)

 6. 编写程序模拟线程死锁

7. 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 


 


1.定时器操作(实现电脑定时关机)。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

//指定到2023-08-01 02:00:00关机
public class T1 {
    public static void main(String[] args) {
        // 创建一个新的线程
        Thread t2 = new Thread(() -> {
            // 创建一个日期格式化对象,用于解析和格式化日期和时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            // 初始化一个日期对象
            Date d = null;
            try {
                d = sdf.parse("2023-08-01 02:00:00");  // 解析指定时间的字符串为日期对象
            } catch (ParseException e) {
                e.printStackTrace();
            }
            long end = d.getTime();  // 获取指定时间的毫秒数

            try {
                while (true) {
                    long now = System.currentTimeMillis();  // 获取当前时间的毫秒数

                    if (now >= end) {
                        System.out.println("时间到");  // 时间到,输出一段文字
                        //Runtime.getRuntime() 是 Java 提供的一个静态方法,它返回当前 Java 虚拟机的运行时对象。
                        // 通过调用这个方法,你可以获取一个 Runtime 实例,用于执行系统级操作,如执行外部程序、管理操作系统的进程等。
                        Runtime run = Runtime.getRuntime();
                        run.exec("cmd /k shutdown /s /t 0");  // 执行关机指令
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        t2.start();  // 启动线程
    }
}
//指定到2023-08-1 02:00:00关机
public class T2 {
    public static void main(String[] args) throws Exception {
                // 创建一个线程池
                ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

                // 设置关机时间
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date shutdownTime = sdf.parse("2022-02-08 11:09:05");

                // 计算距离关机时间的毫秒数
                long delayInMillis = shutdownTime.getTime() - System.currentTimeMillis();

                // 在指定时间执行关机任务
                executor.schedule(() -> {
                    // 执行关机命令
                    try {
                        // Windows系统关机命令
                        String shutdownCommand = "shutdown -s -t 0";
                        // Linux系统关机命令
                        // String shutdownCommand = "sudo shutdown now";

                        // 执行操作系统命令
                        Runtime.getRuntime().exec(shutdownCommand);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }, delayInMillis, TimeUnit.MILLISECONDS);

                // 关闭线程池
                executor.shutdown();
            }
        }
2. 每个月的月末(02:00:00) 执行一次代码
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

//每个月的月末(02:00:00)->是8月1日2点
public class T3 {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            while (true) {
                try {
                    // 获取当前日期
                    var c = Calendar.getInstance();

                    // 设置日期为下个月的第一天(月末)
                    c.set(Calendar.MONTH, c.get(Calendar.MONTH) + 1);
                    c.set(Calendar.DAY_OF_MONTH, 0);

                    // 设置特定时间(小时、分钟、秒)
                    c.set(Calendar.HOUR_OF_DAY, 2);
                    c.set(Calendar.MINUTE, 0);
                    c.set(Calendar.SECOND, 0);

                    // 创建日期格式化对象
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                    // 将设定的时间与当前时间进行比较
                    if (c.getTimeInMillis() == System.currentTimeMillis()) {
                        // -------------- 每月的月末,要执行的代码

                        // 输出设定的日期和时间
                        System.out.println(sdf.format(c.getTime()));
                        System.out.printf("%1$tF %1$tT%n", System.currentTimeMillis());
                        
                        // 使线程睡眠2秒,机器运行速度过快,防止1S内执行多次
                        TimeUnit.SECONDS.sleep(2);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
3. 模拟售票

模拟了多个线程同时售票的场景,保证了并发环境下的票的售卖过程的正确性和一致性。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class T4 {
    List<String> set = new ArrayList<>(); // 票的集合
    int num = 50; // 票的数量,默认为50张

    public T4() {
        this.num = 50;
        for (int i = 0; i < this.num; i++) {
            set.add(String.format("%03d", i + 1)); // 将票号以三位数格式添加到集合中
        }
    }

    public T4(int num) {
        this.num = num;
        for (int i = 0; i < this.num; i++) {
            set.add(String.format("%03d", i + 1)); // 将票号以三位数格式添加到集合中
        }
    }

    public void out() {
        Random rand = new Random();
        String t = Thread.currentThread().getName(); // 获取当前线程的名称
        while (this.set.size() > 0) { // 当票池中还有票时
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟处理过程,让线程睡眠1秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (this) { // 使用synchronized关键字实现对票池的同步访问
                try {
                    TimeUnit.SECONDS.sleep(1); // 模拟处理过程,让线程睡眠1秒
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    int index = rand.nextInt(this.set.size()); // 随机选择票池中的一个索引
                    String p = this.set.get(index); // 获取对应索引的票号
                    this.set.remove(p); // 从票池中移除该票号
                    if (this.set.size() == 0) {
                        System.out.printf("%s:售出%s票,票已经售完。%n%n", t, p); // 输出剩余最后一张票信息
                    } else {
                        System.out.printf("%s:售出%s票,还有%d张票%n", t, p, this.set.size()); // 输出售票信息
                    }
                } catch (Exception e) {
                    System.out.printf("%s:票已经售完。%n", t); // 输出票已经售完的信息
                }
            }
        }
    }

    public static void main(String[] args) {
        T4 t = new T4();
        new Thread(t::out, " 东风路").start(); // 创建并启动线程
        new Thread(t::out, " 南阳路").start(); // 创建并启动线程
        new Thread(t::out, " 瑞达路").start(); // 创建并启动线程
        new Thread(t::out, "科学大道").start(); // 创建并启动线程
    }
}

4. 15个线程实现,求123456789 之间放+-和为100的表达式(11个结果),如果一个线程求出结果, 立即告诉其它停止。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class T5 {
    //volatile是Java中的关键字,它用于声明一个变量,在多线程环境中确保可见性和有序性。
    //当一个变量被声明为volatile时,线程在访问该变量时会直接从主内存中读取变量的最新值,而不是从线程的工作内存中读取。
    // 并且,对声明为volatile的变量的写操作会立即刷新到主内存中,而不是留存在线程的工作内存中。
    volatile List<String> list = new ArrayList<>(); // 用于存储满足条件的计算字符串

    void op() {
        System.out.printf("%s 启动计算中 %n", Thread.currentThread().getName());

        String[] o = new String[]{"", "+", "-"}; // 符号数组
        Random rand = new Random(); // 随机数生成器
        StringBuffer str = new StringBuffer("1"); // 初始化字符串为"1"

        // 使用正则表达式匹配数字
        Pattern p = Pattern.compile("(\\d+|-\\d+)");

        // 当满足条件的计算字符串数量不为11时,不断生成新的计算字符串
        while (list.size() != 11) {
            for (int i = 2; i < 10; i++) {
                str.append(String.format("%s%d", o[rand.nextInt(o.length)], i)); // 随机生成运算符和数字拼接到计算字符串中
            }

            String s = str.toString(); // 获取生成的计算字符串
            Matcher m = p.matcher(s); // 进行匹配

            List<Integer> ln = new ArrayList<>(); // 用于存储匹配到的数字
            while (m.find()) {
                ln.add(Integer.parseInt(m.group())); // 将匹配到的数字转换为整数并添加到列表中
            }

            int sum = ln.stream().reduce(0, Integer::sum); // 对列表中的数字求和
            if (sum == 100 && !list.contains(s)) { // 如果求和结果为100且列表中不包含该计算字符串,则将其添加到列表中
                list.add(s);
                System.out.printf("[%s]:%s = 100%n", Thread.currentThread().getName(), s);
            } else {
                str.delete(1, str.length()); // 清空计算字符串
            }
        }

        System.out.printf("%s 结束 %n", Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        var t = new T5();
        for (int i = 0; i < 15; i++) {
            new Thread(t::op, "T" + i).start(); // 创建并启动15个线程
        }
    }
}

 5. 实现一个容器,提供两个方法,addcount 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。(10个线添加元素,1个线程监控统计)
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class T6 {
    List<String> list = new ArrayList<>(); // 存储字符串的列表
    CountDownLatch latch = new CountDownLatch(1); // 用于线程同步,初始计数为1

    void add() {
        for (int i = 0; i < 10; i++) {
            try {
                TimeUnit.SECONDS.sleep(1); // 休眠1秒钟
            } catch (Exception e) {
                e.printStackTrace();
            }

            String msg = String.format("%s-%d", "hello", i); // 格式化字符串
            list.add(msg); // 添加字符串到列表
            System.out.printf("%s : %s%n", Thread.currentThread().getName(), msg); // 输出线程名称和添加的字符串

            if (list.size() == 5) { // 当列表中元素数量达到5时
                latch.countDown(); // 减少计数器的值到0
            }
        }
    }

    void count() {
        try {
            latch.await(); // 等待计数器达到0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("%s 已经有5个元素%n", Thread.currentThread().getName()); // 输出统计信息
    }

    public static void main(String[] args) {
        var t = new T6();

        // 创建并启动添加元素的线程
        new Thread(t::add, "T1").start();

        // 创建并启动统计元素个数的线程
        new Thread(t::count, "T2").start();
    }
}

 6. 编写程序模拟线程死锁

这段代码涉及多线程的同步问题,使用了两个对象a和b作为锁。

在主线程的main方法中,创建了一个T14对象t,然后启动了两个线程T1T2,分别调用tm1m2方法。

m1方法中,线程先获取对象a的锁,然后睡眠1秒,再尝试获取对象b的锁。在synchronized块中,该线程可以安全地访问对象b

m2方法中,线程先获取对象b的锁,然后睡眠1秒,再尝试获取对象a的锁。在synchronized块中,该线程可以安全地访问对象a

这段代码存在死锁的风险,如果线程T1在获取对象a的锁后,睡眠了1秒,然后线程T2在获取对象b的锁后,尝试获取对象a的锁时,由于线程T1还持有对象a的锁,因此线程T2会被阻塞。同样地,线程T1在获取对象b的锁时也会被阻塞,两个线程都无法继续执行下去,导致程序无法正常结束。

import java.util.concurrent.TimeUnit;

public class T7 {
    final Object a = new Object(); // 创建对象a作为锁
    final Object b = new Object(); // 创建对象b作为锁

    // 线程T1的方法
    void m1() {
        System.out.println(Thread.currentThread().getName() + "启动等待...");

        synchronized (a){ // 获取对象a的锁
            try{
                TimeUnit.SECONDS.sleep(1); // 线程睡眠1秒
            }catch(Exception e){
                e.printStackTrace();
            }
            synchronized (b){ // 获取对象b的锁
                // 在此块中安全地访问对象b
            }
        }
    }

    // 线程T2的方法
    void m2() {
        System.out.println(Thread.currentThread().getName() + "启动等待...");

        synchronized (b){ // 获取对象b的锁
            try{
                TimeUnit.SECONDS.sleep(1); // 线程睡眠1秒
            }catch(Exception e){
                e.printStackTrace();
            }
            synchronized (a){ // 获取对象a的锁
                // 在此块中安全地访问对象a
            }
        }
    }

    public static void main(String[] args) {
        var t = new T7(); // 创建T7对象

        // 创建并启动线程T1
        new Thread(t::m1,"T1").start();

        // 创建并启动线程T2
        new Thread(t::m2,"T2").start();
    }
}

7. 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class T8 {
    AtomicInteger num = new AtomicInteger(0); // 创建一个原子计数器
    ReentrantLock lock = new ReentrantLock(true); // 创建一个公平锁

    // 工作方法
    void work() {
        String tn = Thread.currentThread().getName(); // 获取当前线程名

        while (true) {
            lock.lock(); // 获取锁

            try {
                TimeUnit.SECONDS.sleep(1); // 线程睡眠1秒
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 对计数器进行增加操作,并输出相应的信息
            System.out.printf("%s%d ", tn, num.incrementAndGet());
            //如果当前线程名为"C",则输出换行符。
            if ("C".equals(tn)) {
                System.out.println();
            }
            lock.unlock(); // 释放锁
        }
    }

    public static void main(String[] args) {
        var t = new T8(); // 创建T8对象

        // 创建并启动线程A
        var a = new Thread(t::work, "A");
        a.start();

        // 创建并启动线程B
        var b = new Thread(t::work, "B");
        b.start();

        // 创建并启动线程C
        var c = new Thread(t::work, "C");
        c.start();
    }
}

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

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

相关文章

【LeetCode】383. 赎金信

题目&#xff1a;383. 赎金信 由于此题只含有小写字母,并且magazine里面的字母不可重复使用. 故首先用一个长度为26的整形数组记录magazine里字母出现的次数。 再用这个整形数组跟ransomeNote进行遍历比较&#xff0c;当数组中出现-1时&#xff0c;说明false,否则true. 代码&am…

大数据Flink(五十二):Flink中的批和流以及性能比较

文章目录 Flink中的批和流以及性能比较 ​​​​​​​​​​​​​​一、Flink中的批和流

python与深度学习(九):CNN和cifar10

目录 1. 说明2. cifar10实战2.1 导入相关库2.2 加载数据2.3 数据预处理2.4 数据处理2.5 构建网络模型2.6 模型编译2.7 模型训练2.8 模型保存2.9 模型评价2.10 模型测试2.11 模型训练结果的可视化 3. cifar10的CNN模型可视化结果图4. 完整代码5. 改进后的代码和结果 1. 说明 本…

史上最强,Python自动化测试框架整理,搭建框架看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是测试框架呢…

明星代言注意事项:确保代言活动成功的关键要素

对于商家而言&#xff0c;聘请明星代言自己的品牌产品&#xff0c;无非就是为了利用明星的人气为品牌加持热度&#xff0c;吸引明星粉丝和消费者的关注&#xff0c;增加产品转化&#xff0c;塑造品牌形象。明星代言是一项重要的市场营销策略&#xff0c;但同时也需要注意一些关…

美化与保护合二为一:3个功能厉害的水印app

每个人都有自己珍视的照片回忆&#xff0c;但在互联网上共享这些照片时&#xff0c;担心他人未经允许使用它们是很常见的。幸运的是&#xff0c;现在有了加水印软件&#xff0c;我们可以以有效的方式保护我们的珍贵照片。通过在图片上添加个性化的水印&#xff0c;你可以在不影…

textarea文本高亮选中

最近在实现原文/译文句段高亮对比显示&#xff0c;和有道翻译类似&#xff0c;如下图所示&#xff1a; 最初的解决方案是采用富文本编辑器&#xff0c;把所有句段信息都用HTML标签包裹&#xff0c;操作空间比较大&#xff0c;页面上需要的功能几乎都可以实现&#xff0c;但是由…

串口通讯接口类型:TTL、RS232和RS485(电平标准)

串口通讯接口类型&#xff1a;TTL、RS232和RS485 在串口通信中&#xff0c;常用的接口类型包括TTL、RS-232和RS-485&#xff0c;TTL、RS-232、RS422、RS-485是指的电平标准(电信号)。 通信协议规定了数据传输的规则和格式&#xff0c;包括数据的起始位、停止位、数据位数、校…

docker 搭建jenkins

1、拉取镜像 docker pull jenkins/jenkins:2.4162、创建文件夹 mkdir -p /home/jenkins_mount chmod 777 /home/jenkins_mount3、运行并构建容器 docker run --restartalways -d -p 10240:8080 -p 10241:50000 -v /home/jenkins_mount:/var/jenkins_home -v /etc/localtime:…

从零开始构建基于YOLOv5的目标检测系统

本博文从零开始搭建基于YOLOv5模型的目标检测系统&#xff08;具体系统参考本博主的其他博客&#xff09;&#xff0c;手把手保姆级完成环境的搭建。 &#xff08;1&#xff09;首先Windows R输入cmd命令后打开命令窗口&#xff0c;进入项目目录&#xff0c;本博文以野生动物…

无涯教程-jQuery - jQuery.ajaxSetup( options )方法函数

jQuery.ajaxSetup(options)方法为将来的AJAX请求设置全局设置。 jQuery.ajaxSetup( options ) - 语法 $.ajaxSetup( options ) 这是此方法使用的所有参数的描述- options - 一组配置Ajax请求的键/值对&#xff0c;所有选项都是可选的。 Sr.No.Option & Remark1 asy…

Stack

文章目录 定义分类静态栈动态栈 算法应用 定义 在静态内存当中分配的叫做栈&#xff0c;在动态内存中分配的叫做堆。 **红色椭圆圈当中的就是在栈中分配的&#xff0c;蓝色下划线的就是在堆里分配的。**栈和堆表示的是分配数据的一种方式。静态局部变量是通过压栈和出栈来分配…

网络安全行业相关证书

一&#xff1a;前言 对于考证这个话题&#xff0c;笔者的意见是&#xff1a;“有比没有好&#xff0c;有一定更好&#xff0c;但不一定必须&#xff1b;纸上证明终觉浅&#xff0c;安全还得实力行”。很多人对于各种机构的考证宣传搞得是云里雾里&#xff0c;不知道网络安全行业…

【Java练习题汇总】《第一行代码JAVA》网络编程篇集合体系篇,汇总Java练习题——Socket 与ServerSocket、List和Set、Map~

Java练习题 网络编程篇&集合体系篇 1️⃣ 网络编程篇&集合体系篇 1️⃣ 网络编程篇&集合体系篇 一、填空题 在类集中存放单值的最大父接口是___________ &#xff0c;存放一对值的最大父接口是___________ 。___________ 接口保存的数据是不允许重复的&#xff…

c语言复合赋值符和运算符的优先级问题

结论&#xff1a; 复合赋值符的优先级小于运算符 【练习1】 a * a / b的运算顺序是什么&#xff1f; a / b * a 【练习2】 x / 3 2 * 3 2 / 3 6 2 x 0

使用os库操作目录和文件

1. 获取本机操作系统的分隔符 # 导入os库 import os # 获取本机操作系统的分隔符 print(os.sep) 2. 获取本机操作系统的类型 如果是windows&#xff0c;是nt。 # 导入os库 import os # 获取本机操作系统的类型 print(os.name) 3. 获取当前的工作目录路径 # 导入os库 import…

RL vs 最优控制:用于轨迹跟踪的 LQR(使用 Python 代码)

一、说明 在本博客系列中&#xff0c;我们将了解最优控制的经典方法&#xff0c;这些方法在某种程度上为强化学习等更熟悉的主题奠定了坚实的基础。这两个领域之间存在着不可避免的共同边界&#xff0c;本系列旨在提出这些最优控制的形式化方法&#xff0c;作为强化学习方法的有…

[内网渗透]SUID提权

文章目录 [内网渗透]SUID提权0x01.什么是SUID&#xff1f;0x02.如何设置SUID&#xff1f;0x03.查找属主为root的SUID文件0x04.进行SUID提权1.find提权2.vim/vi/vim.tiny 以root权限修改文件3.bash提权4.less/more执行系统命令5.nano以root权限修改文件6.awk执行系统命令7.cp以r…

CHD6.2.1集群 Hive开启Iceberg

下载jar包 https://repo1.maven.org/maven2/org/apache/iceberg/iceberg-hive-runtime/1.0.0/iceberg-hive-runtime-1.0.0.jar 存放在/opt/cloudera/parcels/CDH/lib/hive/auxlib/ CDH集群修改hive配置 选择xml格式 粘贴即可 <property><name>iceberg.engine.hi…

QT: 完成服务器的实现

1> 思维导图 2> 手动完成服务器的实现&#xff0c;并具体程序要注释清楚 Widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //客户端类 #include <QMessageBox> //…