多线程的实现

继承Thread类实现多线程

java.lang.Thread是一个线程操作的核心类。新建一个线程简单的方法就是直接继承Thread类,而后覆写该类中的 run()方法(就相当于主类中的main方法)

package www.bit.java;

class MyThread extends Thread {
    private int ticket = 100;

    @Override
    public void run() {
        while(this.ticket > 0)
            System.out.println(Thread.currentThread().getName()+"还剩下"+this.ticket--+"票");
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        MyThread myThread1 = new MyThread();
        myThread.start();
        myThread1.start();
    }
}

为什么要通过start()方法来调用run()方法,而不是run()直接执行?

    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        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 */
            }
        }
    }

    private native void start0();

  • 首先我们看到在start()方法中抛出IllegalThreadStateException异常,按照原有的处理方式,应当在调用处进行异常处理,而此处没有处理也不会报错,因此是一个RunTimeException,这个异常的产生只是因为你重复启动了线程才会产生。所以,每一个线程对象只能够启动一次
  • 下面我们看到了在start()方法中调用了start0()方法,而这个方法是一个只声明而未实现的方法同时使用native关键字进行定义。
  • native指的是调用本机的原生系统函数
  • Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(), stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的。
    这个方法放在一个 static 语句块中,当该类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。
	private static native void registerNatives();
    static {
        registerNatives();
    }

本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,它定义了各个操作系统平台都要用到的关于线程的公用数据和操作,

JNIEXPORT void JNICALL Java_Java_lang_Thread_registerNatives (JNIEnv \*env, jclass cls){ //registerNatives
            (\*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
static JNINativeMethod methods[] = {
        {"start0", "()V",(void \*)&JVM_StartThread}, //start0 方法
        {"stop0", "(" OBJ ")V", (void \*)&JVM_StopThread},
        {"isAlive","()Z",(void \*)&JVM_IsThreadAlive},
        {"suspend0","()V",(void \*)&JVM_SuspendThread},
        {"resume0","()V",(void \*)&JVM_ResumeThread},
        {"setPriority0","(I)V",(void \*)&JVM_SetThreadPriority},
        {"yield", "()V",(void \*)&JVM_Yield},
        {"sleep","(J)V",(void \*)&JVM_Sleep},
        {"currentThread","()" THD,(void \*)&JVM_CurrentThread},
        {"countStackFrames","()I",(void \*)&JVM_CountStackFrames},
        {"interrupt0","()V",(void \*)&JVM_Interrupt},
        {"isInterrupted","(Z)Z",(void \*)&JVM_IsInterrupted},
        {"holdsLock","(" OBJ ")Z",(void \*)&JVM_HoldsLock},
        {"getThreads","()[" THD,(void \*)&JVM_GetAllThreads},
        {"dumpThreads","([" THD ")[[" STE, (void *)&JVM_DumpThreads},
};

观察上边一小段代码,可以容易的看出 Java 线程调用 start->start0 的方法,实际上会调用到 JVM_StartThread 方法,那这个方法又是怎么处理的呢?
实际上,我们需要看到的是该方法终要调用 Java 线程的 run 方法,事实的确也是这样的。
在 jvm.cpp 中,有如下代码段:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){    
	...    
	native_thread = new JavaThread(&thread_entry, sz);   
	... 
}

这里JVM_ENTRY是一个宏,用来定义JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程, 其线程函数是 thread_entry,如下:

static void thread_entry(JavaThread* thread, TRAPS) {    
    HandleMark hm(THREAD);    
    Handle obj(THREAD, thread->threadObj());    
    JavaValue result(T_VOID);    
    JavaCalls::call_virtual(&result,obj, KlassHandle(THREAD,SystemDictionary::Thread_klass()),    
    vmSymbolHandles::run_method_name(),    //LOOK! 看这里    
    vmSymbolHandles::void_method_signature(),THREAD); 
}

可以看到调用了 vmSymbolHandles::run_method_name 方法,而run_method_name是在 vmSymbols.hpp 用宏定义的:

class vmSymbolHandles: AllStatic {   
	...   
	template(run_method_name,"run")  //LOOK!!! 这里决定了调用的方法名称是 “run”!   
	... 
}

Runnable()接口实现多线程

Thread类的核心功能是进行线程的启动。如果一个类为了实现多线程直接去继承Thread类就会有单继承局限。在 java中又提供有另外一种实现模式:Runnable接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

利用Runable接口实现线程主体类

  • 利用Runable接口实现线程主体类
class MyThread implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {
        while (this.ticket > 0)
           System.out.println(Thread.currentThread().getName()+"还剩下"+this.ticket--+"票");
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread,"黄牛1").start();
        new Thread(myThread,"黄牛2").start();
    }
}
  • 使用匿名内部类进行Runnable对象创建
public class TestThread {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            private int ticket = 100;

            @Override
            public void run() {
                while(this.ticket > 0)
                    System.out.println(Thread.currentThread().getName() + "还剩下" + this.ticket-- + "票");
            }
        };
        new Thread(runnable,"黄牛1").start();
        new Thread(runnable,"黄牛2").start();
    }
}
  • 使用Lamdba表达式进行Runnable对象创建
public class TestThread {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int ticket = 10;
            while (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "还剩下" + ticket-- + "票");
            }
        };
        new Thread(runnable,"黄牛1").start();
        new Thread(runnable,"黄牛2").start();
    }
}

继承Thread类与实现Runnable接口的关系

  • Thread类与自定义线程类(实现了Runnable接口),是一个典型的代理设计模式。Thread类负责辅助真实业务操作(资源调度,创建线程并启动)。自定义线程类负责真实业务的实现(run方法具体要做的事)。
  • 使用Runnable接口实现的多线程程序类可以更好的描述贡献的概念。

Callable实现多线程

从JDK1.5开始追加了新的开发包:java.uti.concurrent。这个开发包主要是进行高并发编程使用的,包含很多在高并发操作中会使用的类。在这个包里定义有一个新的接口Callable

@FunctionalInterface
public 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;
}

Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String>{
    private int ticket = 1000;
    @Override
    public String call() {
        while (this.ticket > 0){
            System.out.println(Thread.currentThread().getName() + "还剩下" + ticket-- + "票");
        }
        return "票卖完了!!!";
    }
}
public class TestThread {
    public static void main(String[] args) throws ExecutionException,InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask,"黄牛1").start();
        new Thread(futureTask,"黄牛2").start();
        System.out.println(futureTask.get());
    }
}

在这里插入图片描述

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