java中,启动线程通常是通过Thread或其子类通过调用start()方法启动。
常见使用线程有两种:实现Runnable接口和继承Thread。而继承Thread亦或使用TimerTask其底层依旧是实现了Runnabel接口。考虑到java的单继承的限制,所以在开发过程中大部分情况在使用线程的时候是通过实现Runnabel接口或者Runnbel匿名类来实现的。
例如:
package com.zpj.thread.blogTest; /** * Created by PerkinsZhu on 2017/8/11 16:42. */ public class ThreadTest { public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); //所有的这些启动方式的执行体都是在run方法中完成的 threadTest.testLambda(); threadTest.testRunnableWithAnonymousRunnable(); threadTest.testRunnableWithAnonymousThread(); threadTest.testRunnable(); threadTest.testMyThread(); } public void testLambda() {//lambda表达式开启线程 jdk1.8中的特性 new Thread(() -> System.out.println("i am lambda Thread....")).start(); } public void testRunnableWithAnonymousThread() {//匿名Thread类开启线程 new Thread() { @Override public void run() { System.out.println("i am ThreadWithAnoymous"); } }.start(); } public void testRunnableWithAnonymousRunnable() {//匿名Runnable类开启线程 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("i am RunableWithAnoymous"); } }); thread.start(); } public void testRunnable() {//实现Runnable接口 MyRunnable runable = new MyRunnable(); Thread thread = new Thread(runable); thread.start(); } public void testMyThread() {//继承自Thread MyThread thread = new MyThread(); thread.setName("MyThread"); thread.start(); } } class MyRunnable implements Runnable {//实现Runnable接口 @Override public void run() { System.out.println("i am MyRunnable"); } } class MyThread extends Thread {//继承Thread @Override public void run() { System.out.println(" i am MyThread!!"); } }
注意,直接调用run()方法的方式执行线程体并未开启新线程,只是在main方法中调用了一个普通方法而已。而使用start()方法则会开启一个新的线程执行。两者的区别主要表现在前者是阻塞式的,而后者为非阻塞式。
例如:
public void testStartAndRun(){ MyThread thread = new MyThread(); thread.setName("MyThread"); thread.start();//start启动两者异步非阻塞运行 while (true){ System.out.println("---- "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread extends Thread {//继承Thread @Override public void run() { while(true){ System.out.println("=== "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果如下:
---- main
==== MyThread
---- main
==== MyThread
==== MyThread
---- main
==== MyThread
---- main
而把thread.start() 修改为thread.run();之后,则如下
==== main ==== main ==== main ==== main ==== main ==== main
这里之所以线程名称为main是因为是在main线程中调用的run方法,所以打印出来的是===main。而thread.run();语句下面的循环则永远不会执行,程序将会一直在run方法中循环下去。
那么调用start方法,程序都做了些什么呢?看一下底层实现。
public synchronized void start() { //验证线程的状态 if (threadStatus != 0) {//这里的验证涉及到线程不能重复启动的问题,线程多次调用start则会抛出该异常 throw new IllegalThreadStateException(); } //把该线程加入到线程组中 group.add(this); boolean started = false;//标识线程是否启动成功 try { start0();//调用native方法启动线程 该方法是阻塞的,程序等待其完成之后执行下面语句如果执行失败则直接抛出异常进入finally。 started = true;//修改线程启动状态 } finally { try { if (!started) {//启动失败,则移出线程组 group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0();//native方法启动线程
通过Runnable启动具有一定的局限,执行线程没有返回值,无法捕获异常。在有些特殊情况下需要线程返回结果的时候就不太合适。这时可以选择Callable接口来完成。Callable涉及到Future模型,这到后面再说。
=============================================
原文链接:多线程(三) java中线程的简单使用 转载请注明出处!
=============================================
-----end