多线程学习笔记(七)之wait与sleep的区别、线程停止及守护线程等

来源:互联网 发布:购买淘宝小号 编辑:程序博客网 时间:2024/05/18 15:06

wait()方法与sleep()方法的区别

wait()方法与sleep()方法的区别:

  1. wait可以指定时间也可以不指定时间;sleep必须指定时间
  2. 在同步中时,对cpu的执行权和锁的处理不同:
    wait:释放cpu执行权,释放锁
    sleep:释放cpu执行权,不释放锁

停止线程

定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

例如通过flag的方式:

class StopThread implements Runnable{    private boolean flag = true;    public void run(){        while (flag){            System.out.println(Thread.currentThread().getName()+"......");        }    }    public void setFlag(){        flag = false;    }}public class StopThreadDemo {    public static void main(String[] args){        StopThread st = new StopThread();        Thread t1 = new Thread(st);        Thread t2 = new Thread(st);        t1.start();        t2.start();        int num = 1;        for (;;){            if (++num==50){                st.setFlag();                break;            }            System.out.println("main..."+num);        }        System.out.println("over");    }}

但是上述通过flag的方式有时候也不能使线程停止,例如我们修改下StopThread类的代码:

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

当线程Thread0获得执行权时,由于wait陷入等待状态,之后当Thread1获得执行权时,由于wait陷入等待状态,此时主线程执行结束,flag设置为false,但是却无法停止线程Thread0与Thread1,两者一直处于等待状态。也就是说,如果线程处于冻结状态(sleep时间很长的时候也是同理),无法读取标记,那么如何使这种情况的线程结束?下面的方式就可以解决这种问题。

使用interrupt(中断)方法

该方法是结束线程的冻结状态,使线程回到运行状态中来。
(注:stop方法已经过时不再使用)

public void interrupt():中断线程

如果线程在调用Object类的wait(),wait(long)或wait(long,int)方法,或者该类的join(),join(long),join(long,int),sleep(long)或sleep(long,int)方法过程中受阻,则其中断状态将被清除(中断状态指程序冻结,即停止运行),它还将收到一个InterruptedException。即interrupt方法可以不等到sleep时间到即可以把其唤醒,是强制性的,因此是在程序应该等待的时候使其中断,会抛出InterruptedException异常。

因此对上述程序进行修改:

class StopThread implements Runnable{    private boolean flag = true;    public synchronized void run(){        while (flag){            try{                wait();            }catch (InterruptedException e){                System.out.println(Thread.currentThread().getName()+"......"+e);                flag = false;            }            System.out.println(Thread.currentThread().getName()+"......");        }    }    public void setFlag(){        flag = false;    }}public class StopThreadDemo {    public static void main(String[] args){        StopThread st = new StopThread();        Thread t1 = new Thread(st);        Thread t2 = new Thread(st);        t1.start();        t2.start();        int num = 1;        for (;;){            if (++num==50){                t1.interrupt();                t2.interrupt();                break;            }            System.out.println("main..."+num);        }        System.out.println("over");    }}

运行结果:

这里写图片描述

t1.interrupt()与t2.interrupt()将线程从冻结状态中强制恢复到运行状态中来,让线程具备cpu的执行资格,但是会发生异常InterruptedException,因此原本处于try块中等待的线程t0与t1再interrupt方法之后抛出异常,进入catch块,通过在catch块中将flag置为false使其跳出while循环,最终达到停止线程的目的。

interrupt()只是改变中断状态而已
  interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
  如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。

wait() & interrupt()
  线程A调用了wait()进入了等待状态,也可以用interrupt()取消.
  不过这时候要小心锁定的问题.线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt()时(注意等待的线程调用其自己的interrupt(),例如上述demo中的t1与t2调用各自的interrupt方法),会先重新获取锁定,再抛出异常.在获取锁定之前,是无法抛出异常的.

守护线程

void setDaemon(boolean on):将该线程标记为守护线程(后台线程)或者用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。
参数:on——如果为true,则将该线程标记为守护线程。

例如仍是上面的例子的main方法修改下:

public class StopThreadDemo {    public static void main(String[] args){        StopThread st = new StopThread();        Thread t1 = new Thread(st);        Thread t2 = new Thread(st);        t1.start();        t2.setDaemon(true);        t2.start();        int num = 1;        for (;;){            if (++num==50){                t1.interrupt();                //t2.interrupt();                break;            }            System.out.println("main..."+num);        }        System.out.println("over");    }}

虽然没有使用t2.interrupt(),但是由于将线程t2设置成为了守护线程,因此当最后由于等待wait只剩下t2线程时,其仍会停止,即只剩下守护线程时,其将停止,无论处于什么状态。

join方法

public final void join() throws InterruptedException :等待该线程终止,**抛出**InterruptException-如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
(注:会抛出异常的时候,要么在所在函数的声明位置加上throws Exception,要么将其放入try{}catch{}块中)

class DemoTest implements Runnable{    public void run(){        for (int x=0;x<50;x++){            System.out.println(Thread.currentThread().getName()+"......"+x);        }    }}public class JoinDemo {    public static void main(String[] args) throws Exception{        DemoTest d = new DemoTest();        Thread t1 = new Thread(d);        Thread t2 = new Thread(d);        t1.start();        //临时加入一个线程运算时可以使用join方法        t1.join();//t1线程要申请加入进来运行,主线程等待t1终止之后再执行,即此时主线程处于冻结状态,是可以使用interrupt方法强制恢复回来的        t2.start();        t2.setPriority(Thread.MAX_PRIORITY);        //t1.join();//t1线程要申请加入进来运行,主线程等待t1终止之后再执行,注意此时开启的线程是t1与t2,即t1与t2互相争夺执行权,但是主线程仅仅与t1线程相关联,在线程t1执行结束后主线程开始执行        for (int x=0;x<50;x++){            System.out.println(Thread.currentThread().getName()+"......"+x);        }    }

优先级

优先级越高代表被cpu执行的概率越大(1到10),一般1、5与10的差距是最大的。
因为是静态常量,因此需要加上类名
字段摘要:Thread.MAX_PRIORITY,线程可以具有的最高优先级;Thread.MIN_PRIORITY,线程可以具有的最低优先级;Thread.NORM_PRIORITY,分配给线程的默认优先级,例如:

t2.setPriority(Thread.MAX_PRIORITY);

Thread.yield()方法

yield():暂停当前正在执行的线程对象,并执行其他线程
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。
因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。例如:

class DemoTest implements Runnable{    public void run(){        for (int x=0;x<50;x++){            System.out.println(Thread.currentThread().getName()+"......"+x);            Thread.yield();        }    }}
1 0
原创粉丝点击