java的线程知识怎么回答_Java线程知识:一、对Java线程的初步认识

一、线程与进程的关系

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

进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

线程(thread) 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

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

进程(Process): 程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源。

线程(thread) : CPU调度的最小单位,必须依赖进程而存在。

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

二、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是单继承,只能继承一个类,不能继承多个类。而在我们某些实际的业务中,我们可能需要继承一个类来处理逻辑业务,那么就不能再继承Thread类来处理线程相关的操作了。所以JDK又为我们提供了一个实现Runnable接口的方式,而且在Java中,一个类是可以实现多个接口的,这样我们在使用第二种方式处理线程就不会有顾忌了。(这里比较有意思的是:Thread类实现了Runnable接口,有兴趣的小伙伴可以去看一下源码。)

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

/**

* @author cai

* 通过实现Callable接口的方式

* 带返回值

*/

private static class UserCallable implements Callable{

/**

* 重写 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 future = new FutureTask(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() : 会导致线程不会正常的释放资源,“恶意”的中断当前正在运行的线程,不管线程的逻辑是否完整。

suspend()与resume() : 极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象,产生死锁。(这个例子以后在讲线程锁的时候会解释。)

我们先来看一下调用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()

interrupt():调用此方法会中断一个线程,但并不是强行关闭这个线程,只是先给这个线程打个招呼,同时将线程的中断标志位设置为true,线程是否中断,由线程本身决定。

isInterrupted():判断当前的线程是否处于中断状态(返回true或者false)。

interrupted():静态方法,判断当前的线程是否处于中断状态,同时将中断的标志位设置为false。

注意:如果方法中抛出了InterruptedException异常,那么线程的中断标志位会被复位成false,如果确实需要中断线程的操作,我们需要在catch语句中再次调用interrupt()方法。

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

/**

* @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();

}

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

1、@1 的位置调用的interrupt()方法,所以这里的标志位是true,所以 @2 的位置打印结果为true。

2、@3 位置的sleep方法会抛出InterruptedException异常,我这里捕获了,在看之前的理论,抛出此异常,标志位会重置为false,所以@4 这里的打印结果为false。

3、@5 位置再次调用了interrupt(),又把标志位改为了false,所以 @6 这里打印的结果为true,但是这里注意的是,@6 调用了interrupted()这个静态方法,所以标志位又变为了false,所以@7 打印的结果为false。

控制台打印结果:

caiThread catch flag is true

caiThread catch interrput flag is false

caiThread interrput flag is true

caiThread interrput flag is false

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

对线程的了解再多一点点

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

新建、就绪、阻塞、运行、死亡

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

0f057a3f9722a5d8c626cb9ead633cdf.png

这张图上面中的方法我会在以后的博客中分享到,这次的分享就到这里,希望大家能够喜欢。

最后

注:若有转载,请标明原处。如若有错,也欢迎小伙伴前来指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值