前言
如题,今天搜多线程按序打印的时候,发现大佬们基本都用了Lock或者特殊类来完成“按序”,即使使用了synchronize,也还是用了一些已有类。所以我想如何不用特殊类,只用java本身的基本语法和多线程本身的特性完成同样的效果,所以有了以下代码。
代码1-定义多个实现Runnable的类
/**
* @author 作者 wuyuzhuo:
* @version 创建时间:2021年3月20日 下午5:06:56
* 类说明
*/
public class Test1_1 {
static Integer flag=-1;
static int count = 1;
static final Object OBJECT = new Object();
public static class MyThread1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(count<=75) {
synchronized (OBJECT) {
if(flag==0) {
for (int i = 0; i < 5; i++) {
System.out.println("线程1:"+count);
count++;
}System.out.println();
flag++;
flag%=3;
// OBJECT.notifyAll();
}
try {
OBJECT.notifyAll();
OBJECT.wait();
}catch (Exception e) {
// TODO: handle exception
}
}
}
}
}
public static class MyThread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(count<=75) {
synchronized (OBJECT) {
if(flag==1) {
for (int i = 0; i < 5; i++) {
System.out.println("线程2:"+count);
count++;
}System.out.println();
flag++;
flag%=3;
// OBJECT.notifyAll();
}
try {
OBJECT.notifyAll();
OBJECT.wait();
}catch (Exception e) {
// TODO: handle exception
}
}
}
}
}
public static class MyThread3 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(count<=75) {
synchronized (OBJECT) {
if(flag==2) {
for (int i = 0; i < 5; i++) {
System.out.println("线程3:"+count);
count++;
}System.out.println();
flag++;
flag%=3;
// OBJECT.notifyAll();
}
try {
OBJECT.notifyAll();
OBJECT.wait();
}catch (Exception e) {
// TODO: handle exception
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
flag=0;
MyThread1 thread1 = new MyThread1();
MyThread2 thread2 = new MyThread2();
MyThread3 thread3 = new MyThread3();
Thread t1=new Thread(thread1);//.start();
Thread t2=new Thread(thread2);//.start();
Thread t3=new Thread(thread3);//.start();
t1.start();
t2.start();
t3.start();
Thread.sleep(10);
t1.interrupt();
t2.interrupt();
t3.interrupt();
}
}
代码2-定义一个实现Runnable的类
/**
* @author 作者 wuyuzhuo:
* @version 创建时间:2021年3月21日 上午12:30:02
* 类说明
*/
public class Test1_2 {
public static class MyRunnable implements Runnable{
static int flag;
static int count=1;
static final Object object=new Object();
int signal;
public MyRunnable(int s) {
// TODO Auto-generated constructor stub
signal=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(count<=75) {
synchronized (object) {
if((signal-1)==flag) {
for(int i=0;i<5;i++) {
System.out.println("线程"+signal+":"+count);
count++;
}System.out.println();
flag++;
flag%=3;
}
try {
object.notifyAll();
object.wait();
}catch (InterruptedException e) {
// TODO: handle exception
System.out.print("Thread "+signal+" has been interrupted!");
// e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread t1=new Thread(new MyRunnable(1));
Thread t2=new Thread(new MyRunnable(2));
Thread t3=new Thread(new MyRunnable(3));
t1.start();
t2.start();
t3.start();
Thread.sleep(5);
t1.interrupt();
t2.interrupt();
t3.interrupt();
}
}
解释说明
- 上程序实现了三线程按续打印1~75,每个线程每次打印5个。
- 代码1三个线程是专门写了三个类分别实现的,这是为了便于理解,实际上可以优化成只写一个线程类(见代码2)
- 运行结束后,希望线程关闭。有的博客说线程会在
while(count<=75)
跳出后自动关闭,之前我也是这么以为的(所以被坑惨了),但事实上从我在eclipse和在牛客网分别跑的结果来看,其实并没有。牛客网会报超时,而eclipse控制台始终不会terminate(终止)。所以这也是为什么主程序要加以下代码的原因:Thread.sleep(10); t1.interrupt(); t2.interrupt(); t3.interrupt();
——通过interrupt中断来优雅的停止线程,同时为了保证我们确实是在所有线程完成本职工作后才中断(这能保证线程确实被终止,而非之后仍会被唤醒导致停止失效)【这里总感觉有更好地方法,即无论运行了多久,在检测到终止条件后当前线程终止,但暂时还没查到资料】 - 本代码中使用一个静态的Object对象来管理synchronize,这相当于一个信号量;使用flag来保证线程先后顺序,进入后若不符合要求则自动让出资源。之所以不用flag来管理,是因为synchronize不能用int来管理。这些和我们在计算机组成原理一面学到的同步锁和信号量部分相对应。
运行结果