一、线程与进程的关系


关于进程与线程,百度百科上是这样描述的:

简单的总结一下,就变成了下面的结果:

也就是说 线程进程的关系,就像玄幻小说中虫族的母体和子体一样,子体离开了母体便不能存活。线程亦是如此,必须依赖于进程而存在。

二、Java线程的启动方式


关于Java的线程,我们首当其冲的会想到java.lang.Thread类,这是一种最简单的创建线程的方式,我们只需要通过继承Thread类就可以实现:

/**
 * @author cai
 * 通过继承Thread类的方式
 */
private static class UserThread extends Thread{

    /**
     * 重写  Thread 类的  run() 方法
     */
    @Override
    public void run() {
        System.out.println("this is a thread for extends Thread");
    }
}

这里,我们重写了Thread类中的run()方法,并打印一条语句来表示线程的启动方式,现在我们来测试一下:

public static void main(String[] args) {

    // 继承Thread类的方式
    Thread thread = new UserThread();
    thread.start();
}

控制台的打印结果:

this is a thread for extends Thread

从打印结果可以看出,我们的线程正常的启动,中间没有出现意外。除了这种方式之外,JDK内部又给我们提供了一个接口类:java.lang.Runnable,同样,我们也可以通过实现该接口,重写run()方法来启动一个新的线程:

/**
 * @author cai
 * 通过实现Runnable接口的方式
 */
private static class UserRunnable implements Runnable{

    /**
     * 重写  Runnable 类的  run() 方法
     */
    @Override
    public void run() {
        System.out.println("this is a thread for implements Runnable");
    }
}

这里我们同样打印一句话来表示此线程的启动方式,现在来测试一下:

public static void main(String[] args) throws  {
    // 实现Runnable接口的方式
    // 这里注意:所有线程的启动,都必须通过调用Thread类中start()方法来实现
    Runnable runnable = new UserRunnable();
    new Thread(runnable).start();
}

相信上面的代码,小伙伴们都能看懂(多注意一下第二行的注释,这是重点),现在来看看控制台的打印结果:

this is a thread for implements Runnable

同理,这里的线程也正常的启动了。但看到这里,小伙伴们可能就会产生疑问,为什么JDK要多此一举,提供了一种Thread类后,为什么还要提供Runnable接口类呢?

在这里给有这个疑惑的小伙伴们答疑下:

那么除了上面的两种方式之外,Java是否提供了第三种方式呢?答案是肯定的,从JDK1.5开始,JDK为我们提供了一个新的接口类:java.util.concurrent.Callable,我们可以通过实现这个接口来启动一个新得线程,而这种方式与实现Runnable接口来启动线程所不同的是,它会带有一个返回值,我们来看一下代码:

/**
 * @author cai
 * 通过实现Callable接口的方式
 * 带返回值
 */
private static class UserCallable implements Callable<String>{

    /**
     * 重写  Callable 类的  run() 方法
     * @return
     * @throws Exception
     */
    @Override
    public String call() throws Exception {
        return "this is a thread for implements Callable(return String).";
    }
}

测试一下:

public static void main(String[] args) throws ExecutionException, InterruptedException {
    // 实现Callable接口的方式   带返回值
    UserCallable callable = new UserCallable();
    FutureTask<String> future = new FutureTask<String>(callable);
    new Thread(future).start();
    System.out.println(future.get());
}

我们这里将返回值打印一下:

this is a thread for implements Callable(return String).

可以看出,我们的线程正常的启动,没有问题。

那么看了以上三种Java线程的启动方式,相信肯定有很多小伙伴会好奇,如果我要中止一个线程,我需要怎么做呢?让我们来一起看看吧。

三、Java线程的中止


怎样让一个正在运行的线程安全的停止工作呢?这里有两种方法:

1、线程自然的终止:程序正常的执行完或者抛出未处理的异常。

程序正常的执行完就不必再说,以上的代码都属于此类,我们来看一看抛出未处理异常的情况,这里我们将上述实现Runnable接口来启动线程的代码修改一下:

/**
 * @author cai
 * 通过实现Runnable接口的方式
 */
private static class UserRunnable implements Runnable{

    /**
     * 重写  Runnable 类的  run() 方法
     */
    @Override
    public void run() {
        // 重点加了这样的一行代码
        int i = 10 / 0;
        System.out.println("this is a thread for implements Runnable");
    }
}

这里我们加了一行代码,小伙伴们应该都可以看懂,这行代码是必定会抛出异常的,但我们又没有对异常进行处理,现在来看一下控制台的打印结果:

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
	at com.cai.thread.create.NewThread$UserRunnable.run(NewThread.java:39)
	at java.lang.Thread.run(Thread.java:748)

从结果可以看出,程序运行到了int i = 10 / 0这里就停止了,并没有打印出下一行的结果,由此可见,线程到了这里便停止了,没有再走下去。

2、调用JDK为我们提供的一系列方法

stop(),resume(),suspend(),interrupt(),isInterrupted(),interrupted()这些方法都是JDK为我们提供的,但是``stop(),resume(),suspend()`已经不建议使用了,原因如下:

我们先来看一下调用stop()的例子:

/**
 * @author cai
 *  调用 stop() 方法的例子
 */
private static class UserRunnable implements Runnable{

    @Override
    public void run() {
        try {
            // 让此线程休眠1秒钟
            Thread.sleep(1000);
        }catch (Exception e){
            // 异常 捕获处理
        }
        // 此处不会运行,控制台不会打印
        // 若实际开发中,这里要处理一个很重要的业务逻辑,那么这里就会有很大的问题。
        // 所以不建议使用
        System.out.println("代码到此处不会运行");
    }
}

public static void main(String[] args) throws InterruptedException {
    Runnable runnable = new UserRunnable();
    Thread thread = new Thread(runnable);
    thread.start();
    // 强行中止线程
    // 从这里可以看出,JDK已经不建议使用stop()方法了,添加了@Deprecated注解
    thread.stop();
}

我这里将测试代码也一并贴了上去,可以在代码的注释中得到相关的结果。讲完这些,我们来看看剩下的三个方法:interrupt(),isInterrupted(),interrupted()

解释完了,直接上代码吧:

/**
 * @author cai
 * 调用 interrupt(),isInterrupted(),interrupted() 方法的例子
 */
private static class UserThread extends Thread{

    // 给线程一个名字,创建对象时赋值
    public UserThread(String threadName) {
        super(threadName);
    }

    @Override
    public void run() {
        // 获得线程的名字
        String threadName = Thread.currentThread().getName();
       try {
           // @2
           System.out.println(threadName+" flag is " + isInterrupted());
           // 休眠一秒钟   需要捕获异常 InterruptedException  @3
           Thread.sleep(1000);
       }catch (InterruptedException e){
           // 打印一下  isInterrupted() 的状态  @4
           System.out.println(threadName+" catch interrput flag is " + isInterrupted());
           // 调用 interrupt() 方法  中断线程操作  @5
           interrupt();
       }
        // 打印线程的名字  @6
        System.out.println(threadName+" interrput flag is " + interrupted());
        // @7
        System.out.println(threadName+" interrput flag is " + isInterrupted());
    }
}

public static void main(String[] args) throws InterruptedException {
    // interrupt(),isInterrupted(),interrupted()  演示
    Thread thread = new UserThread("caiThread");
    thread.start();
    // @1
    thread.interrupt();
}

这里为了方便解释,我分了步骤:

控制台打印结果:

caiThread catch flag is true
caiThread catch interrput flag is false
caiThread interrput flag is true
caiThread interrput flag is false

小伙伴们可以通过对比结果、代码和解释一起看,相信还是很容易明白的。

对线程的了解再多一点点


Java线程总归下来有五种状态:

而这里对应的方法却有很多种,具体的关系,我这里准备了一张图:

Java线程的启动与中止-LMLPHP

这张图上面的各种方法我都会在下次的文章中分享,这次的分享就到这里,希望大家能够喜欢。

最后


05-26 23:35