一、多线程的创建和执行
方法一:继承Thread类(Thread类是JDK提供的一个抽象类)
用法:
1)继承Thread类,重写run()方法。该run方法将作为线程执行体。
2)创建Thread子类的实例,即创建了一个线程对象。
注意,new了一个Thread实例后,仅仅只是创建了一个线程对象,与其他普通JAVA对象一样,JVM为该线程对象在堆中分配了内存,初始化了成员变量的值,此时该线程仅仅是堆中的一个对象,并没有启动执行。
3)调用线程对象的start方法来启动该线程。
调用start方法后,JVM会为其创建方法调用栈和程序计数器,此时线程处于就绪状态,在就绪队列中等待CPU的调度。
class Thread1 extends Thread {
public void run() {
for(int i=0; i<100000; i++) {
System.out.println("Thread1 is running " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Thread1Test {
public static void main(String[] args) {
new Thread1().start();
}
}
方法二:实现Runnable接口
1)定义Runnable接口的实现类,实现接口的run方法。run方法将作为线程的执行体。
2)将Runnable实现类的实例包装到Thread中,作为Thread的target,创建一个Thread的实例,即创建一个线程对象。
3)调用线程对象的start方法,即启动该线程。
class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("Thread2 is running");
}
}
public class Thread2Test {
public static void main(String[] args) {
new Thread(new Thread2()).start();
}
}
注:Thread2Test的main方法中,调用了Thread类的一个构造函数: (Thread类部分源码)
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
方法三:实现Callable接口
待补充
public class CallableTest implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建一个FutureTask对象,它将执行给定的Callable对象
FutureTask<String> futureTask = new FutureTask<>(new CallableTest());
// 创建线程对象,并启动线程
new Thread(futureTask).start();
// 获取线程执行的返回值
System.out.println(futureTask.get());
}
}
二、多线程运行的规则
1)启动线程必须使用Thread类的start方法,这是Thread类中的一个本地方法,调用start方法后,JVM会以多线程的形式自动执行线程执行体(即run方法)。如果直接调用run方法,将变成串行执行。
2)同一个Thread对象的start方法只能调用一次;已经执行完的线程也不能再次start(线程的run方法执行完成后,线程会自动关闭,此时当然不能再启动了)。否则会触发IllegalThreadStateException异常。
下面代码中,会有一个子线程正常运行,对该线程对象再次start时将触发异常。
public class Thread1Test {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
// 下面两行代码会触发运行时异常: IllegalThreadStateException
thread1.start();
thread1.start();
}
}
可以创建多个线程对象,分别调用start方法,执行同样的方法体(run方法):
下面代码new了两个Thread2对象,因此可以分别调用其start方法。
public class Thread2Test {
public static void main(String[] args) {
new Thread(new Thread2()).start();
new Thread(new Thread2()).start();
}
}
3)线程无需关闭,只要其run方法执行结束,该线程会自动关闭,该线程的生命周期就结束了。
4)多个线程启动,其启动的先后顺序是随机的。
三、多种创建方式的区别
1)继承Thread类的方式,占用了一个继承的名额
2)Thread本身也是继承自Runnable接口,所以继承Thread类本质上也是实现了Runnable接口
3)Runnable启动时需要Thread类的支持,Runnable实现类没办法单独start,必须包装到Thread类中才行。
4)多个线程对象可以共用同一个target(Runnable对象),适合多个线程处理同一份资源的情况。
5)Runnable中更容易实现资源共享。Thread中必须用static变量才能实现变量共享。Runnable中通过普通的变量就可以达到共享。
建议使用实现Runnable接口的方式创建和启动线程