Java线程的启动和停止(一)

如何构造线程

在运行线程之前需要先构造线程对象,线程对象的构造需要指定线程所需要的属性,比如:所属线程组、线程优先级、是否为Daemon线程等信息。下面我们看一下,java.lang.Thread中对线程初始化的方法:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        //线程名称
        this.name = name;
        //当前线程就是该线程的父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * 是否有访问权限
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();
        //线程组
        this.group = g;
        //使用父线程的daemon、priority属性
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* 分配一个线程ID */
        tid = nextThreadID();
    }

一个 新 构造 的 线程 对象 是 由其 parent 线程 来 进行 空间 分配 的, 而 child 线程 继承 了 parent 是否 为 Daemon、 优先级 和 加载 资源 的 contextClassLoader 以及 可继承 的 ThreadLocal, 同时 还会 分配 一个 唯一 的 ID 来 标识 这个 child 线程。 至此, 一个 能够 运行 的 线程 对象 就 初始化 好了, 在 堆 内存 中 等待 着 运行。

我们构造一个Thread thread=new Thread(),打上断点,可以看到thread对象的属性如下:
image

如何使用线程

实现多线程编程的方式主要有两种:一种是继承Thread类,一种是实现Runnable接口,使用继承Thread类最大的局限性是不支持多继承,由于java单继承的特性,为了突破单继承的限制,于是就有了另一个实现方式,就是实现Runnable接口。使用这两种方式创建的线程工作性质是一样的,没有本质的区别。

继承Thread类

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("我是自定义线程MyThread");
    }
}

实现Runnable接口

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我是Runnable");
    }
}

如何启动线程

线程对象初始化完成后,调用线程的start()方法就可以启动线程了,start()方法是告诉Java虚拟机,如果线程规划期空闲,应该立即启动调用了start()方法的线程。

同一个线程不能多次 调用 start() 方法, 否则会出现异常 Exception in thread” main” java. lang. IllegalThreadStateException。

线程的start()方法,会新启动一个线程,而线程run()方法则是同步等待当前线程调用。

如何停止线程

如何判断线程是否停止

在介绍如何停止线程的知识点前, 先看一下如何判断线程是否停止的状态。 Thread.java类中提供了两个很重要的方法:

1) this.interrupted(): 判断当前线程是否已经中断

2) this.isInterrupted(): 判断线程是否已经中断

那么这两种方法的区别是什么呢? 先来看看 this.interrupted()方法: 判断当前线程是否已经中断,当前线程是指运行 this. interrupted()方法 的 线程。

 public static void main(String[] args) {
        Thread.currentThread().interrupt();
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " 当前线程是否已停止:=" + Thread.interrupted());
        System.out.println(threadName + " 当前线程是否已停止:=" + Thread.interrupted());

    }
main 当前线程是否已停止:=true
main 当前线程是否已停止:=false

从执行结果可知 interrupt()方法确实停止了线程,但是第二个判断结果为什么是false呢,通过查看官方文档当解释得知。interrupted() 具有清除状态的功能,所以第二个判断结果为false

下面我们接着查看 isInterrupted()方法:

public static void main(String[] args) {
        Thread.currentThread().interrupt();
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " 当前线程是否已停止:=" + Thread.currentThread().isInterrupted());
        System.out.println(threadName + " 当前线程是否已停止:=" + Thread.currentThread().isInterrupted());

    }

main 当前线程是否已停止:=true
main 当前线程是否已停止:=true

输出的结果都为true,isInterrupted()并未清除状态标志,最后我们得出如下结论:

1) this.interrupted(): 判断线程终止的状态, 执行后具有将状态标志置为false的功能

2) this.isInterrupted():判断线程终止的状态,不具有清除状态标志的功能

try..catch与this.interrupt方法终止线程

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 5000; i++) {
            if (interrupted()) {
                System.out.println(Thread.currentThread().getName() + " 我被停止了,退出循环");
                break;
            }
            System.out.println(" i=" + (i + 1));
        }
    }
}

public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.setName("Thread-interrupt-0");
        thread.start();
        try {
            Thread.sleep(1);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(" 结束!");
    }

输出结果:
.....
i=32
 i=33
 i=34
 i=35
 i=36
 i=37
 i=38
 i=39
 i=40
 i=41
 结束!
Thread-interrupt-0 我被停止了,退出循环

过期的 suspend()、 resume() 和 stop()方法终止线程

suspend()在调用后线程不会释放已经占有的资源,二是占有着资源进入睡眠状态,这样容易引发死锁问题。同样的stop()方法在终止一个线程时不会保证线程的资源正常释放,通常线程没有机会去释放资源,因此会导致程序工作状态的不确定性。

正是因为 suspend()、 resume() 和 stop() 方法 带来的副作用, 这些方法才被标志位不建议使用的过期方法, 而暂停和恢复可以使用 等待/ 通知机制 来替代。

posted @ 2018-09-02 17:16  IT码客  阅读(5129)  评论(0编辑  收藏  举报