黑马程序员:Java基础——多线程的停止与守护以及Join,优先级和yield方法

------- Java EE培训java培训、期待与您交流! ----------

1.概念

线程启动了,做完该做的事情,但是它还是会占用系统资源,那么我们该如何停止呢?

我们知道Java中Thread里面有一个方法叫做stop方法,可是,stop方法已经过时。

那么,如何停止线程?只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

我们先来举个例子:

class StopThread implements Runnable{
	private Boolean flag = true;
	public void run(){
		while(flag){
			System.out.println(Thread.currentThread().getName()+"---run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo {
    public static void main(String[] args) {
        StopThread sThread = new StopThread();

        new Thread(sThread).start();
        new Thread(sThread).start();
        
        int num = 0;
        while(true){
        	if(num++ == 60){
        		sThread.changeFlag();
        		break;
        	}
        	System.out.println(Thread.currentThread().getName()+"++++++"+num);
        }
        System.out.println("Over");
    }
}

我们来看一下运行结果:


只见这哥们一顿运行,到了num==60的时候程序调用了changeFlag()结束了所有线程。

有一种特殊情况,这种情况程序停不下来,那就是使用了synchronized的同步锁。

我们先来看一下代码:

class StopThread implements Runnable{
	private Boolean flag = true;
	public synchronized void run(){
		while(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println(Thread.currentThread().getName()+"...Exception");
			}
			System.out.println(Thread.currentThread().getName()+"---run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo {
    public static void main(String[] args) {
        StopThread sThread = new StopThread();

        new Thread(sThread).start();
        new Thread(sThread).start();
        
        int num = 0;
        while(true){
        	if(num++ == 60){
        		sThread.changeFlag();
        		break;
        	}
        	System.out.println(Thread.currentThread().getName()+"++++++"+num);
        }
        System.out.println("Over");
    }
}

接下来,我们来运行一下:


可以看到,我们的程序只有main线程在输出,而其他两个线程则没有动静,而且,注意图的右上角,红色的小方块证明了这个程序还没有结束。这是为什么呢?

我们回顾一下代码:

我们的flag默认为true,当线程进入synchronized同步锁中判断flag就会直接被位于上面的wait()方法直接叫停,也就是进入冻结状态,同时释放了CPU运行权。这是另一个线程也来了,同样被wait()方法叫停。这两个方法一旦被叫停就无法读取到位于wait()下面的标记,也就无法终止线程了。

我们这样总结:

当线程处于冻结状态时就不会读取到标记,那么线程就不会结束。

那么该如何解决这个问题呢?

在Java的Thread中提供了一种中断线程的方法——interrupt,需要注意的是这个中断不是停止线程,能停止线程的只有stop方法。

* 如果线程在调用Object 类的wait()wait(long)wait(long, int) 方法,或者该类的join()join(long)join(long, int)sleep(long)sleep(long, int) 方法过程中受阻,则其中断状态将被清除

interrupt方法的意思就是强制清除线程的冻结状态,即强制唤醒。但是会将收到一个InterruptedException异常。

我们来修改一下代码:

class StopThread implements Runnable{
	private Boolean flag = true;
	public synchronized void run(){
		while(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println(Thread.currentThread().getName()+"...Exception");
			}
			System.out.println(Thread.currentThread().getName()+"---run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo {
    public static void main(String[] args) {
        StopThread sThread = new StopThread();

        Thread t1 = new Thread(sThread);
        t1.start();
        new Thread(sThread).start();
        
        int num = 0;
        while(true){
        	if(num++ == 60){
        		//sThread.changeFlag();
        		t1.interrupt();
        		break;
        	}
        	System.out.println(Thread.currentThread().getName()+"++++++"+num);
        }
        System.out.println("Over");
    }
}

然后我们运行一下:


注意图中,线程没有终止,而且接收到了异常。为什么没有终止?因为t1被强制唤醒以后又去争CPU执行权时,经过wait()方法时,又一次冻结在那里。同理,另一条线程也这样修改。

这时,我们离结束线程不远了,只要发生了异常我们不妨操作标记来结束线程:

class StopThread implements Runnable{
	private Boolean flag = true;
	public synchronized void run(){
		while(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println(Thread.currentThread().getName()+"...Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread().getName()+"---run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo {
    public static void main(String[] args) {
        StopThread sThread = new StopThread();

        Thread t1 = new Thread(sThread);
        t1.start();
        Thread t2 = new Thread(sThread);
        t2.start();
        
        int num = 0;
        while(true){
        	if(num++ == 60){
        		//sThread.changeFlag();
        		t1.interrupt();
        		t2.interrupt();
        		break;
        	}
        	System.out.println(Thread.currentThread().getName()+"++++++"+num);
        }
        System.out.println("Over");
    }
}
运行结果如下:

我们可以看到,线程异常一样是被输出,但是注意右上角,小方块呈灰色,证明此时程序已经停止运行,也就是说,我们已经将t1,t2两个线程成功的终止掉了。

2.守护线程

Java中提供了一种方法——setDaemon()

setDaemon()将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。

守护线程可以理解为后台线程。

该方法必须在启动线程前调用。

该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。

接下来我们看一下示例:

class DaemonThread implements Runnable {
	private Boolean flag = true;

	public void run() {
		while (flag) {
			System.out.println(Thread.currentThread().getName() + "---run");
		}
	}

	public void changeFlag() {
		flag = false;
	}
}

public class DaemonDemo {
	public static void main(String[] args) {
		StopThread sThread = new StopThread();

		Thread t1 = new Thread(sThread);
		Thread t2 = new Thread(sThread);

		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();

		int num = 0;
		while (true) {
			if (num++ == 60) {
				break;
			}
			System.out.println(Thread.currentThread().getName() + "++++++" + num);
		}
		System.out.println("Over");
	}
}

运行结果是main方法做完了应有的工作,而此时程序关闭。

3.Join()方法

前面Interrupt()方法中我们见到过join()方法,join()是什么呢?

接下来我们来看一下join()方法:

在JDK中是这么说的:等待该线程终止。

我们不妨先来看段代码:

class Join implements Runnable{
	public void run(){
		for(int i = 0;i<70;i++){
			System.out.println(Thread.currentThread().getName()+"---"+i);
		}
	}
}

public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
		Join j = new Join();
		Thread t1 = new Thread(j);
		Thread t2 = new Thread(j);

		t1.start();
		t1.join();     //join是抢夺CPU执行权
		t2.start();
		
		for(int i=0;i<80;i++){
			System.out.println("main______"+i);
		}
		System.out.println("Over!!!");
	}
}

运行结果:通常情况下,我们的t1,t2,main线程都是交叉运行,输出结果的。但是,当我们添加join()方法后,如上面的代码,这个程序输出的顺序就会是这样的:

1.先让t1全部执行完。

2.再让t2和main线程交替运行。

3.执行完毕,最后关闭程序。

我们就可以理解了,join()方法是一个让本线程执行完,再让其他线程执行的方法。

可是接下来我们这样改:

t1.start();
t2.start();
t1.join(); 
我把join()放到了t2.start()后面去,执行结果如下:

这又是怎么回事呢?

当A线程执行到了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。

join可以用来临时加入线程执行。

但是当A线程启动,B线程也会启动,但是当主线程碰到A线程的join方法时,主线程就会进入等待状态,而此时A,B线程都在运行状态,那么这时A,B线程就会交替执行,执行完后主线程就会执行。但是,此时的A,B线程并没有消亡,而是处于挂起状态,当主线程执行完毕后才会一起结束,此时,程序结束。


4.优先级&yield方法

        优先级代表抢资源的频率。

     优先级有10

     其中1,5,10级为最明显。


static int

MAX_PRIORITY
       
线程可以具有的最高优先级。

static int

MIN_PRIORITY
       
线程可以具有的最低优先级。

static int

NORM_PRIORITY
       
分配给线程的默认优先级。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值