【面试经】—— Java多线程的实现方式

Java为我们提供了三种多线程的实现方式:

  • public class Thread extends Object implements Runnable
  • public interface Runnable
  • public interface Callable<V>

Thread:

在Thread类种,JDK为我们提供了一个start()方法。

public void start():

Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.

The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

该方法能够使得线程被启动执行,JVM会执行thread类种的run方法。

调用方法后后会有两个线程同时执行,一个是当前线程(启动thread线程的主线程),另一个是被启动的thread线程(执行run方法的线程)

线程永远都只能启动一个。在一个线程执行完成之前,线程不能被重启

    public static class MyThread extends Thread
    {

        private AtomicInteger tickets = new AtomicInteger(5);

        public void run()
        {
            while (tickets.get() > 0)
            {
                tickets.getAndDecrement();
                System.out.println(Thread.currentThread().getName() + "  卖了一张票, 还剩" + tickets.get() + "张");
            }
        }
    }

    public static void main(String[] args) throws Exception
    {
        MyThread thread = new MyThread();
        new Thread(thread).start();
        new Thread(thread).start();
    }

执行结果:

Thread-1  卖了一张票, 还剩3张
Thread-2  卖了一张票, 还剩3张
Thread-1  卖了一张票, 还剩2张
Thread-2  卖了一张票, 还剩1张
Thread-1  卖了一张票, 还剩0张

Runnable:

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.

In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

我们可以看到,JDK文档种在对创建线程是使用Thread还是Runnable,进行了说明。大多数情况下,建议我们使用Runnable方法来创建我们的线程对象。

如果我们仅仅是为了覆写run方法而不使用(或覆写)Thread方法种的其他方法,都建议使用Runnable接口来创建线程。

这点很重要,因为类应该尽可能不被子类化,除非程序打算修改或增强一个class的功能。

    public static class MyThread implements Runnable
    {

        private AtomicInteger tickets = new AtomicInteger(5);

        public void run()
        {
            while (tickets.get() > 0)
            {
                tickets.getAndDecrement();
                System.out.println(Thread.currentThread().getName() + "  卖了一张票, 还剩" + tickets.get() + "张");
            }
        }
    }

    public static void main(String[] args) throws Exception
    {
        MyThread thread = new MyThread();
        new Thread(thread).start();
        new Thread(thread).start();
    }

执行结果:

Thread-0  卖了一张票, 还剩4张
Thread-0  卖了一张票, 还剩2张
Thread-1  卖了一张票, 还剩2张
Thread-0  卖了一张票, 还剩1张
Thread-1  卖了一张票, 还剩0张
 

Callable:

A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.

The Executors class contains utility methods to convert from other common forms to Callable classes.

JDK中定义:Callable是一个与Runnable类似的接口,他们都是一个线程类接口。 但是Runnable是没有返回值的,同时也是不能抛出一个Checked异常。

我们知道,如果启动一个线程需要借助Thread类来启动。但是,在Thread类的构造方法中,是没有相关方法来接受Callable对象的。因此,我们必须借助一个类来将Callable转换成Thread类能够接受的对象来启动。

public class FutureTask<V> extends Object implements RunnableFuture<V>:

在JDK文档中,有着如下一段描述:

A FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable, a FutureTask can be submitted to an Executor for execution.

我们的FutureTask能够封装Callable类或者Runnable类,而且FutureTask是Runaable接口的子类。因此我们能够借助FutureTask类来启动Callable类的线程。

    public static class MyThread implements Callable<String>
    {

        private AtomicInteger tickets = new AtomicInteger(5);

        public String call()
        {
            while (tickets.get() > 0)
            {
                tickets.getAndDecrement();
                System.out.println(Thread.currentThread().getName() + "  卖了一张票, 还剩" + tickets.get() + "张");
            }
            return Thread.currentThread().getName() +" 票卖完了";
        }
    }

    public static void main(String[] args) throws Exception
    {
        MyThread thread = new MyThread();
        FutureTask<String> task1 = new FutureTask<String>(thread);
        FutureTask<String> task2 = new FutureTask<String>(thread);
        new Thread(task1).start();
        new Thread(task2).start();

        System.out.println(task1.get());
        System.out.println(task2.get());
    }

执行结果:

Thread-0  卖了一张票, 还剩4张
Thread-1  卖了一张票, 还剩3张
Thread-0  卖了一张票, 还剩2张
Thread-1  卖了一张票, 还剩1张
Thread-0  卖了一张票, 还剩0张
Thread-0 票卖完了
Thread-1 票卖完了
 

猜你喜欢

转载自blog.csdn.net/syy19940213/article/details/81777571