黑马程序员:Java基础——多线程的停止与守护以及Join,优先级和yield方法,joinyield
黑马程序员:Java基础——多线程的停止与守护以及Join,优先级和yield方法,joinyield
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 |
相关文章
- 暂无相关文章
用户点评