目录
线程与进程
Thread类
创建线程的两种方式
方式1:继承Thread类
方式2:实现Runnable接口
* 方式2延伸:实现Callable接口
Callable接口
Futuretask类
返回线程计算结果:get()
Callable:创建子线程
补充点
(1)采用实现Runnable、Callable接口的方式创建多线程的优缺点
(2)采用继承Thread类的方式创建多线程的优缺点
线程与进程
线程,不同于我们所熟知的进程,是更为细粒度的程序执行单元。换言之,一个进程可以表示一个应用程序,但是一个线程只能用于表示这个应用程序的一条执行路径。例如:QQ进程,可能包含了n个线程,用于管理界面交互、收发聊天消息、接口系统信息等等。
如下图所示,我们可以看到,当前计算机中运行着的线程数远远大于进程数。
Thread类
在Java编程语言将线程抽象为Thread类,用Thread类来描述线程的概念,并拟定了优先级(priority)、守护线程(daemon Thread)、等相关的属性。
jdk8源码文档注释内容如下,
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
线程是一个进程/程序(Program)的一条执行路径,JVM虚拟机允许一个应用同时执行多个线程。Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon.
When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the
creating thread, and is a daemon thread if and only if the creating thread is a daemon.
线程拥有一个优先级别(priority),优先级高的优先于优先级低的线程执行。一个线程可能是守护线程。
当线程A在执行过程中创建了一个新的线程a,则:线程a默认拥有与线程A同样的优先级。当且仅当线程A是一个守护线程时,线程a才会成为守护线程。When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class).
Java虚拟机一经启动,就会有一个非守护线程——对应于main方法开始执行。The Java Virtual Machine continues to execute threads until either of the following occurs:
(1)The <code>exit</code> method of class Runtime has been called and the security manager has permitted the exit operation to take place.
(2)All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
JVM虚拟机结束执行多线程状态的两种情况:
(1)Runtime类的exit()方法被调用,并且,security manager管理器也允许退出操作被执行。
(2)所有的非守护线程都已死亡,可以是run()方法正常执行结束,也可以是run()方法执行过程中抛出了异常,都会导致该线程结束执行。
创建线程的两种方式
jdk1.8源码的文档注释中指出,java有两种创建线程的方式。
There are two ways to create a new thread of execution.
方式1:继承Thread类
One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. 方式1:定义一个Thread类的子类SubThread,然后重写Thread类的run()方法,然后创建一个SubThread子类的实例,将其启动即可;
如下为参考jdk1.8中给出的示例程序编写的线程测试demo:创建一个子线程,用于打印子线程所持有的成员属性minPrime的值。
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
@Override
public void run() {
// compute primes larger than minPrime
System.out.println(this.minPrime);
}
}
public class Thread_Class {
//properties
//methods
public static void main(String[] args) {
//创建子线程
Thread thread = new PrimeThread(100);
//启动子线程
thread.start();
}
}
方式2:实现Runnable接口
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.
第二种方式是:创建一个Runnable接口的子类SunRunnable,然后实现run()方法。接着创建SunRunnable子类对象,将其作为参数传递给Thread类,将其启动即可。
以下为参考jdk1.8源码示例程序完善的创建子线程的demo,同样:用于打印一个数字。
//方式2-实现Runnable接口
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
System.out.println(minPrime);
}
}
public class Thread_Class {
//properties
//methods
public static void main(String[] args) {
//创建子线程
Runnable runnable = new PrimeRun(10);
//启动子线程
new Thread(runnable).start();
}
}
* 方式2延伸:实现Callable接口
Callable接口
由于Runnable接口的run()方法只能用来执行子线程任务,但是没有返回值。因此,jdk又引入了Callable接口,用于创建一个带返回值的线程。
jdk8源码文档注释描述如下,
A task that returns a result and may throw an exception.
Implementors define a single method with no arguments called
{@code call}.
Callable接口用于创建一个返回结果的线程(可能抛出异常)。该接口的实现者需要重写一个无参方法call().
<p>The {@code Callable} interface is similar to {@link java.lang.Runnable}, in that both are designed for classes whose instances are potentially executed by another thread. A {@code Runnable}, however, does not return a result and cannotthrow a checked exception.
Runnable接口的run()方法既没有返回值,也不会抛出异常,而Callable接口的call()方法就可以。当无法计算返回值的时候,Callable接口的call()方法就抛出异常;如果可计算出结果,就将其返回,并正常结束子线程的执行。
Callable作为一个函数式接口,其源码如下,
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Futuretask类
使用Callable接口创建子线程,需要借助Futuretask类,该类作为Runnable接口的子类,提供了有参构造可以接收Callable接口的实例。因此,创建子线程时,可将FutureTask实例作为参数传递给Thread类的构造器完成。
返回线程计算结果:get()
FutureTask子类提供了get()方法,可等待子线程计算结果,并将其返回。通过这个方法,可以获取子线程的计算结果值。
Callable:创建子线程
使用Callable接口创建子线程的示例代码如下,具体是:通过Callable接口、FutureTask类、Thread类创建了一个子线程,获取用于返回该子线程持有的成员属性的值,随后通过get()方法,获取子线程的计算结果。
class PrimeCallable implements Callable{
long timelong ;
public PrimeCallable(long timelong){
this.timelong = timelong;
}
@Override
public Object call() throws Exception {
return this.timelong;
}
}
public class Callable_interface {
//properties
//methods
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable接口子类
Callable callable = new PrimeCallable(1000);
//创建Runnable接口实例
FutureTask runnable = new FutureTask(callable);
//创建子线程
Thread thread = new Thread(runnable);
//启动子线程
thread.start();
//获取接收线程计算的结果值
Object o = runnable.get();
System.out.println(o);
}
}
补充点
(1)采用实现Runnable、Callable接口的方式创建多线程的优缺点
-
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
-
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
-
劣势是,编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法
(2)采用继承Thread类的方式创建多线程的优缺点
-
劣势是,因为线程类已经继承了Thread类,所以不能再继承其他父类。
-
优势是,编写简单,如果需要访问当前线程,则无须使用Thread.currentThread()方法,直接使用this即可获得当前线程。
PS:鉴于上面分析,因此一般推荐采用实现Runnable接口、Callable接口的方式来创建多线程。