【面試經】—— 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 票賣完了
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章