创建线程有四种方法
JVM是操作系统的一个过程,在一个过程下可以有多个线程。在Java中,只有Thread类可以通过一个被Native关键字(当Java调用非Java代码编写的接口时,用这个关键字进行修改)来修改名为Start0()的向当地操作系统申请线程的资源。
一、继承Thread类Threadstart0()方法只能用于在java中申请操作系统的线程资源。
1.新建类继承Thread类,重写run方法,run方法是开启线程的主要业务。
public class ThreadTest extends Thread {@Overridepublic void run() {//todo Systemm需要执行的业务.out.println("my thread test");}}
在Thread源代码中,明确指出Thread的子类需要继承该方法。如果该线程由Runnable对象构建,则调用Runnnable实现类中的Run()方法。
/** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }
2.start()方法在main方法中打开线程
public class TestMain {public static void main(String[] args) {ThreadTest threadTest = new ThreadTest(); //ThreadTestat方法调用start方法.start();}}
为什么不调用run()方法来调用start()方法呢?
在Threarun()方法的源代码中,我们可以看到run方法中没有打开线程,只是一种普通的方法。调用run()方法不会在当前线程中启动新的run()方法,而是在threadstart()方法中真正实现多线程。
public synchronized void start() { ///判断线程转态是否已启动,多次启动会报错 if (threadStatus != 0) throw new IllegalThreadStateException(); ///将线程添加到线程组中 group.add(this); boolean started = false; try { ///启动线程 start0(); started = true; } finally { try { if (!started) { //启动失败,状态回滚,然后继续尝试启动这个线程 group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }
在start()的源代码中,可以看到线程实际上是通过start0()启动的。该方法将运行新的线程,并调用run()方法。
重复调用start()方法意味着当线程多次启动时,它会被抛出 IllegalthreadStatexception异常。
二、实现Runnable接口实现Runnnable接口的新线程类,重写Run方法。
public class RunnableTest implements Runnable{@Overridepublic void run() {System.out.println("my runnable test");}}
此时此类无法构建新的线程。还需要将其传输到Thread类,并通过Thread的satart()方法调用。
public class TestMain {public static void main(String[] args) {RunnableTest runnableTest = new RunnableTest();Thread thread = new Thread(runnableTest);thread.start();}}
为什么要把runnabletest传入Thread和Thread?.start()能调用runnnnabletest的run()方法?
这就需要看Thread的构造函数了。
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { //... this.target = target; //... }
init()法在构造函数中被调用,在这种方法中可以看到输入的runnable被赋值给this.target,在我们之前看到的run()方法的源代码中,当target不在空时执行时,target.run(),所以最后在调用run方法时,调用我们传入runnable的run()方法。
三、实现Callable接口Callable可以说是Runnnable最大的特别不同之处在于,callable有返回值,他与future同用,callable产生结果,而future则用于获取结果。futureget()用于等待Calable结束并获得其执行结果,这种方法会造成阻塞。
@FunctionalInterfacepublic 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;}
从Callabe的源代码可以看出,它是一个函数接口,只有一个call()方法,call()方法中要调用的代码与run()方法中的代码相同。
我们首先设置一个类来实现Callable接口,此时定义的返回类型是String
public class CallableTest implements Callable<String> {@Overridepublic String call() throws Exception {return "my callable test";}}
在main方法中,我们需要将callable的实现类传输到futuretask进行包装,然后将futuretask传输到theard启动线程执行。
public class TestMain {public static void main(String[] args) throws ExecutionException, InterruptedException {CallableTest callableTest = new CallableTest();FutureTask<String> futureTask = new FutureTask<>(callableTest);Thread thread = new Thread(futureTask);thread.start();System.out.println(futureTask.get());}}
首先,让我们来看看FunnableFuture接口,它实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable接口和Future接口
public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run();}
可以看出,futuretask中有一种实现run()的具体方法,而thead则是如此.start()调用futuretask中的run()方法。让我们来看看futuretask的结构函数。在结构函数中,calable被赋值为类本身的私有属性
private Callable<V> callable;
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
在futuretaskrun()方法中
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { //等待call()方法返回结果 result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
可以看到 result = c.call();,c是刚刚传入Futuretask的callable,实际上是调用callable实现类的call方法进行返回。
/** * Sets the result of this future to the given value unless * this future has already been set or has been cancelled. * * <p>This method is invoked internally by the {@link #run} method * upon successful completion of the computation. * * @param v the value */ protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } }
最后将result结果传入set方法,赋值给privatetet Object outcome,当我们调用get方法时,会输出这个变量的值。
四、线程池创建线程使用线程池创建可以很好地重用线程。我们使用Thread创建一个,它将在线程执行后被销毁。线程的创建和销毁是一个非常耗尽系统资源的问题。我们可以把线程池看作是一个制造和存储线程的工厂。当线程池有线程时,直接使用它,而不是新的,然后在使用后返回到线程池。有许多配置参数需要自己配置,这里我们使用newfixedThreadPol,可以创建固定尺寸的线程池。
public class TestMain {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(2);Future<?> future = executorService.submit(new RunnableTest());Future<?> future1 = executorService.submit(new CallableTest());executorService.execute(new RunnableTest());System.out.println(future1.get());}}
在这里,我们创建了一个包含两个线程的线程池,分别调用submit()和execute()方法。我们可以看到两者之间的区别
/** * Submits a Runnable task for execution and returns a Future * representing that task. The Future's {@code get} method will * return {@code null} upon <em>successful</em> completion. * * @param task the task to submit * @return a Future representing pending completion of the task * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if the task is null */ Future<?> submit(Runnable task);
submit()方法属于ExecutorService,并且有返回值,可以输入Runnable和callable
/** * Executes the given command at some time in the future. The command * may execute in a new thread, in a pooled thread, or in the calling * thread, at the discretion of the {@code Executor} implementation. * * @param command the runnable task * @throws RejectedExecutionException if this task cannot be * accepted for execution * @throws NullPointerException if command is null */ void execute(Runnable command);
Execute是Executor的方法,只能传入Runnable,没有返回值。
小结1.线程总是调用Threadrun()方法。
2.使用Runnable创建线程只需要实现接口,而继承Thread类后,其他类别(java单继承)不能继承,使用Runnable创建线程可以更灵活。
3.根据使用情况,使用Callable可以获得线程的返回值。
4.以上四种方法都可以创建线程。他们的最终底层是Thread类,通过Thread类Start0()申请线程资源
