继承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());
}
}