本文目的
实现多线程的中断与再运行。
代码示例
1.使用单例模式创建一个类去实现管理线程组
2.利用线程的suspend方法与resume方法实现线程的中断与再运行。线程终止利用线程的stop方法。
代码示例
管理线程组的单例类
package com.example.testdemo.block.thread;
import java.util.HashMap;
import java.util.Map;
public class Singleton {
public Map<String, Map<String, TestThread>> threds = new HashMap<String, Map<String, TestThread>>();
public static Singleton single = new Singleton();
private Singleton() {
}
public static Singleton getSingleton() {
return single;
}
}
我选择单例模式是为了实现数据共享,同时还可以起到节省内存资源。为了操作简单,直接采用饿汉式的方式。
模拟线程类
package com.example.testdemo.block.thread;
import java.util.HashMap;
import java.util.Map;
public class TestThread extends Thread {
private int start;
private int end;
private String descName;
private String taskkey;
public TestThread(int start, int end, String descName, String taskkey) {
this.start = start;
this.end = end;
this.descName = descName;
this.taskkey = taskkey;
}
@Override
public void run() {
for (; start < end; start++) {
for (int i = 0; i < 30000; i++) {
for (int j = 0; j < 30000; j++) {
for (int j2 = 0; j2 < 80000; j2++) {
}
}
}
System.out.println(start + ">-" + descName + ">-" + taskkey);
}
addNewTask();
}
public void addNewTask() {
Singleton s = Singleton.getSingleton();
synchronized (s) {
s.threds.get(taskkey).remove(descName);
TestThread testThread = new TestThread(200, 210, descName, taskkey);
Map<String, TestThread> map = new HashMap<String, TestThread>();
map.put(descName, testThread);
s.threds.get(taskkey).putAll(map);
testThread.start();
}
}
@Override
public String toString() {
return "TestThread [start=" + start + ", end=" + end + ", descName=" + descName + ", taskkey=" + taskkey + "]";
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public String getDescName() {
return descName;
}
public void setDescName(String descName) {
this.descName = descName;
}
public String getTaskkey() {
return taskkey;
}
public void setTaskkey(String taskkey) {
this.taskkey = taskkey;
}
}
四个属性:start:开始位置,end:结束位置(用于控制循环记数),descName:描述名称,taskkey:任务键。
在任务最后,会再次新建一个线程,新建的线程标识和结束的线程一样。
上面的空的for循环,只是为了输出慢一点。不用sleep,是因为这个方法偶尔会抛出异常。不安逸。
接口代码
package com.example.testdemo.controller;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.example.testdemo.block.thread.Singleton;
import com.example.testdemo.block.thread.TestThread;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("test/thread")
@Slf4j
public class TestOne {
@GetMapping("/start")
@ResponseStatus(code = HttpStatus.OK)
public void testStart() {
Singleton s = Singleton.getSingleton();
buildTask(s);
}
private void buildTask(Singleton s) {
TestThread testThrod1 = new TestThread(1, 60, "key1", "task1");
TestThread testThrod2 = new TestThread(-60, 0, "key2", "task1");
Map<String, TestThread> map = new HashMap<String, TestThread>();
map.put(testThrod1.getDescName(), testThrod1);
map.put(testThrod2.getDescName(), testThrod2);
testThrod1.start();
testThrod2.start();
s.threds.put("task1", map);
}
@SuppressWarnings("deprecation")
@GetMapping("/end")
@ResponseStatus(code = HttpStatus.OK)
public void endStart() throws InterruptedException {
Singleton s = Singleton.getSingleton();
synchronized (s) {
if (null != s.threds.get("task1")) {
s.threds.get("task1").values().forEach(t -> {
LocalDateTime localDateTime = LocalDateTime.now();
log.info(localDateTime + "");
t.suspend();
});
}
}
Thread.sleep(4000);
synchronized (s) {
if (null != s.threds.get("task1")) {
s.threds.get("task1").values().forEach(t -> {
LocalDateTime localDateTime = LocalDateTime.now();
log.info(localDateTime + "");
t.resume();
});
}
}
}
}
1.start接口就会启动线程。这儿是启动两个线程。
2.end就会中断线程,四秒之后中断的线程会再次运行。
3.启动通过任务键给线程归类,中断则是通过任务键中断对应的线程组。
缺陷
suspend,resume,stop这样的方法都被标注为过期方法,因为其不会保证释放资源,容易产生死锁,所以不建议使用。
结果演示
swagger接口示例页面.png
调用start接口后端输出.png
调用end接口后端输出(1).png
调用end接口输出(2).png
通过图片可以看到,调用end接口,停止了输出,暂停4秒之后继续输出。证明调用end接口确实将线程挂起了。
tips:本文的中断官方术语叫挂起。