线程有三种创建方式:
继承Thread类
实现Runnable接口
实现Callable接口
在还不懂进程和线程?本文给你一一扫盲一文中粗浅地介绍了进程和线程的概念,该文只是帮助大家认识线程,jdk官方对线程的解释如下:
Athreadis a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
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.
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). The Java Virtual Machine continues to execute threads until either of the following occurs:
– The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
– 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.
There are two ways to create a new thread of execution. 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.
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.
线程(Java中Thread类)是程序执行的一个线程。Java虚拟机允许一个应用程序并发地执行多个线程。
每个线程有一个优先级。高优先级的线程会比低优先级的线程优先执行。每个线程可以被设置或不设置为守护线程。当某个线程的代码中创建了一个新的线程对象,那么新的线程对象拥有和创建它的线程对象相同的优先级。创建新线程的线程是守护线程的时候,新线程才会是守护线程。
当一个Java虚拟机启动的时候,通常只有一个非守护线程(这个非守护线程会调用指定类的main方法)。Java虚拟机会一直执行线程直到以下情况之一出现:
– Runtime类的exit方法被调用并且安全管理器运行退出。
– 所有的非守护线程运行结束,要么是run方法正常结束,要么是run方法异常退出。
有两种方法可以创建一个线程。一个是声明一个Thread类的子类,该子类要重写run方法,然后该子类的实例对象便可以被分配和启动了。
另一个创建线程的方法是实现run方法,并且实现run方法。该类的实例对象可以被分配,将它作为参数传给Thread对象创建的时候,便可以启动了。
继承Thread类
继承Thread类方法分为3步:
继承Thread实现run方法
创建该类的线程对象
调用start方法启动线程
来看代码实现:
继承Thread实现run方法
创建该类的线程对象
调用start方法启动线程
SubThread类重写的run方法干了一件事就是打印0~9,并且每打印一个数字,休眠1s。
程序执行效果如下:
task1和task2分别表示了线程1和线程2,可以看到线程1和线程2交替在执行。
在这里要特别注意的是run方法和start 方法。run方法是我们重写的方法,而start方法是我们调用启动线程执行的方法,这两个千万不要搞混了。线程创建好不会立即执行,需要我们手动调用让它开始执行,run方法是我们告诉计算机要执行什么事情,start则是计算机可以开始执行了。
实现Runnable接口
实现Runnable接口方法分为4步:
实现Runnable接口并实现run方法
创建该类对象
将上述对象作为参数传给Thread的构造方法创建Thread对象
调用start方法启动线程
相比继承Thread类的方法主要是多了一步传递对象参数创建Thread对象的步骤,而实现run方法和调用start方法是不变的,注意这里也不要搞混run方法和start方法,下面来看下代码实现:
实现Runnable接口并实现run方法
创建该类对象
将上述对象作为参数传给Thread的构造方法创建Thread对象
调用start方法启动线程
RunnableImpl类实现的run方法和SubThread类功能一样,执行结果如下:
task1和task2分别表示了线程1和线程2,可以看到线程1和线程2交替在执行,基本和继承Thread方法保持一致。
实现Callable接口
前面两种方法继承Thread类和实现Runnable接口有一个很明显的缺陷就是,在执行完任务之后无法获取执行结果。如果要获取执行结果,需要定义变量其他线程再来获取这个共享变量,比较麻烦。在Java1.5以后,提供了Callable可以获取到线程执行结果。
实现Callable接口方法分为6步:
继承Callable接口实现call方法
创建ExecutorService对象
创建Callable实现类的对象
调用submit方法提交执行
获取结果线程执行结果
关闭ExecutorService
来看代码实现:
继承Callable接口实现call方法
创建ExecutorService对象
创建Callable实现类的对象
调用submit方法提交执行
获取结果线程执行结果
关闭ExecutorService
CallableImpl类实现的run方法和SubThread类功能一样,执行结果如下:
因为Callable有返回值,所以最后打印了输出结果,如下:
创建线程方式小结
综合来看,创建线程方式只有两种,一种是实现Runnable接口,一种是实现Callable接口,因为Thread类实际上也是实现了Runnable接口:
Thread实现Runnable接口
在日常代码开发中,我们也是使用的实现Runnable接口比较多,因为继承Thread类会有单继承问题。所以我们只要记住Runnable和Callable接口就可以了,记住实现两个方法:run和call。
而从步骤上来讲,核心的有三步:
实现接口(Runnable或Callable)
创建线程(Thread类或ExecutorService线程池)
启动执行(start或submit)