线程的基本操作

来源:互联网 发布:数字音效软件 编辑:程序博客网 时间:2024/06/05 06:12

线程的基本操作

新建线程

java中建立线程可以有两种方式,分别是继承Thread类和实现Runnable接口。

public static class CreateThread extends Thread{    @Override    public void run() {        System.out.println("Oh, I am CreateThread");    }}public class CreateThread implements Runnable {    @Override    public void run() {        System.out.println("Oh, I am Runnable");    }}

开启线程

开启线程的方式分为两种start(),run()

Thread t1 = new Thread();t1.start()Thread t2 = new Thread();t2.run()

两者的区别为:

start()是开启一个新的线程并执行run()函数,run()只在当前线程之中串行执行run()的代码。

终止线程

通常情况线程执行完毕后就会结束,不需要手动关闭,例如绝大部分的worker类型的线程。但是有些服务端的后台线程会常驻系统(死循环方式)。

JDK中提供了Thread.stop()方法,不过是一个废弃的方法,原因是:stop()方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。例如:Thread.stop()方法在结束线程的时候,会直接终止线程,并且会立即释放这个线程所持有的锁。而这些锁恰恰是用来维持对象一致性的。

更好的结束线程的方式是使用一个变量标识是否需要结束,或者使用中断的方式:

//每次都是执行完一个循环体之后再退出//线程何时退出由程序员决定public static class ChangeObjectThread extends Thread {    volatile boolean stopme = false;    public void stopMe(){        stopme = true;    }    @Override    public void run() {        while (true) {            if (stopme){                System.out.println("exit by stop me");                break;            }            //do something        }    }}//这里使用了中断判定来退出public static class ChangeObjectThread extends Thread {    @Override    public void run() {        while (true) {            if (Thread.currentThread().isInterrupted()){                System.out.println("exit with interrupted");                break;            }            //do something        }    }}

线程中断

与线程中断有关的,有三个方法:

public void Thread.interrupt()              //中断线程public boolean Thread.isInterrupted()       //判断是否被中断public static boolean Thread.interrupted()  //判断是否被中断,并清除中断标志,static
public static void main(String[] args) throws InterruptedException {    Thread t1=new Thread(){        @Override        public void run(){            while(true){                // if(Thread.currentThread().isInterrupted()){                //  System.out.println("Interruted!");                //  break;                // }                Thread.yield();            }        }    };    t1.start();    Thread.sleep(2000);    t1.interrupt();}

在这里虽然对t1进行了中断,但是在t1中并没有中断处理的逻辑(注释部分),因此即使t1线程被设置了中断状态,但是这个中断不会发生任何作用。

等待(wait)和通知(notify)

为了支持多线程之间的协作,JDK提供了两个非常重要的接口线程等待wait()方法和通知notify()\notifyAll()方法。这两个方法并不是在Thread类中的,而是输出Object类。

这也意味着任何对象都可以调用这两个方法。

public class SimpleWN {    final static Object object = new Object();    public static class T1 extends Thread{        public void run()        {            synchronized (object) {                System.out.println(System.currentTimeMillis()+":T1 start! ");                try {                    System.out.println(System.currentTimeMillis()+":T1 wait for object ");                    object.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(System.currentTimeMillis()+":T1 end!");            }        }    }    public static class T2 extends Thread{        public void run()        {            synchronized (object) {                System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");                object.notify();                System.out.println(System.currentTimeMillis()+":T2 end!");                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                }            }        }    }    public static void main(String[] args) {        Thread t1 = new T1() ;        Thread t2 = new T2() ;        t1.start();        t2.start();    }}

上述的代码如果去除掉synchronized块,代码将无法运行。因为出现了java.lang.IllegalMonitorStateException。

首先,这儿要非常注意的几个事实是:

1)任何一个时刻,对象的控制权(monitor)只能被一个线程拥有。无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)

2)如果在没有控制权的线程里执行对象的以上三种方法,就会报java.lang.IllegalMonitorStateException异常。

3)JVM基于多线程,默认情况下不能保证运行时线程的时序性

基于以上几点事实,我们需要确保让线程拥有对象的控制权。也就是说在T1中执行wait方法时,要保证T1对object有控制权;在T2中执行notify方法时,要保证T2对object有控制权。

挂起(suspend)和继续执行(resume)线程

suspend()和resume()是一对相反的操作,在JDK中已经是废弃的方法。不推荐suspend()去挂起线程的原因是因为suspend()在导致线程暂停的同时,并不会去释放任何资源。

更为严重的是如果resume()在suspend()之前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且线程所持有的资源将不会被释放。

等待结束(join)和谦让(yield)

和字面意思一样。

线程组

public class ThreadGroupName implements Runnable {    public static void main(String[] args) {        //建立一个线程组        ThreadGroup tg = new ThreadGroup("PrintGroup");        //创建线程的时候将线程加入到线程组,使用了Thread的构造函数:        //public Thread(ThreadGroup group, Runnable target, String name)        Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");        Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");        t1.start();        t2.start();        //线程组中活动线程的总数,但是由于线程是动态的,所以这个只是一个估计值        System.out.println(tg.activeCount());        //列出线程组中的线程        tg.list();    }    @Override    public void run() {        String groupAndName = Thread.currentThread().getThreadGroup().getName()                + "-" + Thread.currentThread().getName();        while (true) {            System.out.println("I am " + groupAndName);            try {                Thread.sleep(3000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

运行结果:


运行结果
运行结果

线程组还有一个应用就是ThreadGroup.stop()方法,不过其所存在的问题和Thread.stop()一样,因此使用时需要格外谨慎。

守护线程

守护线程是一种特殊的线程,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相对应的是用户线程,用户线程可以认为j是系统的工作线程。如果用户线程全部结束,这也意味这这个程序实际上无事可做了。守护线程要守护的对象已经不存在了,那么整个应用程序就自然应该结束。因此,当一个Java应用内,只有守护线程的时候,Java虚拟机就会自然退出。

简单看下守护线程的使用:

public class DaemonDemo {    public static class DaemonT extends Thread{        public void run(){            while(true){                System.out.println("I am alive");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) throws InterruptedException {        Thread t=new DaemonT();        //必须在start()之前设置为守护线程        t.setDaemon(true);        t.start();        Thread.sleep(2000);    }}

注意:守护线程Thread.setDaemon(true)必须要在线程start()之前设置,否则抛出java.lang.IllegalThreadStateException。设置无效,也不影响原来线程作为工作线程继续工作

线程优先级

在java中,使用1到10表示线程优先级(数字越大优先级越高)。一般可以使用Thread类中定义的三个静态标量来表示:


线程优先级
线程优先级


这里写图片描述
Thread.setPriority()例子