1.继承thread类java
2.实现runnable接口。ide
3.实现callable接口。函数
这三种方式具体如何建立如何调用详见下面代码,注释已经写得很详细了。例子中分别用这三种方式建立线程并分别启动了两个线程。具体区别本身细微体会下。spa
package com.thread.Lone; /** * 建立线程的第一种方法 * 继承java.lang.Thread,而后重写该类的run方法. */ public class ThreadExt extends java.lang.Thread { public void run(){ System.out.println(Thread.currentThread().getName()+" start"); for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } System.out.println(Thread.currentThread().getName()+" end"); } }
package com.thread.Lone; /** * 第二种建立线程的方法,实现Runnable接口 * 由于java不支持多继承,那么假设你的类自己继承了一个父类 * 这时候还想建立线程的话,那么实现java.lang.Runnable就比较合适了。 */ public class ThreadRunnable implements java.lang.Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start"); for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } System.out.println(Thread.currentThread().getName()+" end"); } }
package com.thread.Lone; import java.util.concurrent.Callable; /** * 前面两种方式,都没有返回值,那若是咱们但愿线程能把执行结果返回到主程序,就须要用到concurrent包里面的东西 * java.util.concurrent.FutureTask 和 java.util.concurrent.Callable * 这里须要实现Callable接口,主要是为了实现call()方法,而call方法就是带有返回值的。 * 启动方式见主程序。 */ public class ThreadFuture implements Callable{ @Override public Object call() throws Exception { System.out.println(Thread.currentThread().getName()+" start"); int i; for(i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } System.out.println(Thread.currentThread().getName()+" end"); //为了体现Future的get()的等待效果,这里休息2秒 Thread.sleep(2000); return Thread.currentThread().getName()+" 总共循环了"+i+"次"; } }
package com.thread.Lone; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * 在这个类里面咱们会分别启动不一样方式建立的线程。 */ public class TestRun { public static void main(String[] args) throws ExecutionException, InterruptedException { // 启动继承Thread类的线程 // 执行这段代码你能够发现console打出的log基本每次都不同 // 甚至有时候ext2的log会输出在ext1以前,能够看出线程启动以后,具体的执行已经和主程序无关,也不会受到其余线程的影响。 ThreadExt ext1 = new ThreadExt(); //这里有一个好习惯就是,对于你启动的线程最好命名一下方便排查问题。 ext1.setName("ThreadExt one"); ThreadExt ext2 = new ThreadExt(); ext2.setName("ThreadExt two"); //经过阅读源码能够发现,最终调用start()方法是去调用一个本地的start0方法 //private native void start0(); 因为我不懂C也就不去看了。 ext1.start(); ext2.start(); //下面启动实现runnable接口的线程 //启动方法略有不一样,由于runnable接口自己是没有start()方法的。 //因此要启动方式是,新建一个Thread对象并把本身的对象当作Target传进去 //而在Thread的run()方法的注释里面有这么写到:当构建Thread的时候target不为空,调用target的Run,而若是为空,则什么都不作。 ThreadRunnable threadRunnable1 = new ThreadRunnable(); //这里可使用带名字的构造函数 Thread t1 = new Thread(threadRunnable1,"threadRannable1 "); t1.start(); //匿名方式 new Thread(threadRunnable1,"threadRannable2 ").start(); //下面启动实现Callable的线程 //和实现Runnable的线程相似,最终的启动方式也是经过Thread.start来启动的。 //可是Thread只接受Runnalbe类型的target,而咱们的类实现的是callable接口,明显不匹配 //这时候就须要引入一个FutureTask类,这个类自己实现了runnable可是持有一个callable对象。是一个典型的适配器模式,将一个Callable对象假装成一个Runnable对象 ThreadFuture threadFuture = new ThreadFuture(); //把threadFuture给传入到futureTask FutureTask futureTask = new FutureTask(threadFuture); //这时候,咱们就能够把持有threadFuture对象的FutureTask当作target传给Thread对象。 //经过源码能够看到,当start()执行的时候,会走到futureTask的Run方法,而futureTask的run方法,又回去调用ThreadFuture的call方法,而且把返回值保存到FutureTask的outcome属性上。 new Thread(futureTask,"ThreadFuture1 ").start(); //当咱们调用get()的时候,其实会去判断当前的状态,若是线程已经跑完(state=1),就拿到outCome给咱们,而若是没有跑完就会等待跑完。 System.out.println(futureTask.get()); //因此若是单独调ThreadFuture相关代码的话,就能够看到不一样,这里在ThreadFuture1执行完以前是不会执行ThreadFuture2的,由于get会处在等待状态,一直等到当前线程执行完成才会继续走下面的逻辑. //另外须要注意的是,futureTask在线程执行完以后,自己的状态已经变成Normal了,这时候再次启动同一个futureTask的时候,其实是什么都不作的。 // 例如:new Thread(futureTask,"ThreadFuture2 ").start(); 其实并不会走到ThreadFuture //想要新启动一个线程须要new一个futureTask。 FutureTask futureTask2 = new FutureTask(threadFuture); new Thread(futureTask2,"ThreadFuture2 ").start(); System.out.println(futureTask2.get()); } }