目录
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. 实现一个容器,提供两个方法,add,count 写两个线程,线程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,然后启动了两个线程T1和T2,分别调用t的m1和m2方法。在
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();
}
}


















![[内网渗透]SUID提权](https://img-blog.csdnimg.cn/img_convert/04a0a8c0c60c510a02ebecb27a711ec0.png)

