二、线程的创建
Java编程语言的JVM虚拟机允许程序运行多个线程,具体是通过java.lang.Thread 类来体现。
Thread类的特点:
-
将需要线程执行的程序编写在Thread类的run()方法中,所以将run()的方法体称为线程体;
-
通过Thread类对象的start()方法启动该线程,并不是直接调用run()本身。
2.1 Thread构造器
在不包含线程组构造器的情况下,Thread类有四个构造器
public Thread(); // 分配一个Thread对象
public Thread(String name) // 分配一个Thread对象,指定Thread名称
public Thread(Runnable target) // 指定创建线程的目标对象,它实现了Runnable接口中的run方法
public Thread(Runnable target, String name) // 指定创建线程的目标对象,它实现了Runnable接口中的run方法,指定Thread线程名称
2.2 创建线程的方式
在Java SDK中有四种创建新线程的方式,分为两个阶段
-
JDK5.0之前有两种方式
通过继承Thread类的方式;
通过实现Runnable接口的方式。
方式一、继承Thread类
定义子类继承Thread类;
子类中重写Thread类的run()方法;
创建Thread类子类的对象,即线程对象;
调用线程对象的start()方法,启动线程,执行run()
方式二、实现Runnable接口
定义子类实现Runnable接口;
子类中重写Runnable接口中的run方法;
通过Thread类的构造器,将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中;
调用线程的start()方法,启动线程,调用了接口子类的run()方法。
-
JDK5.0新增两种方式
通过实现Callable接口的方式;
通过线程池的方式。
方式三、实现Callable接口
定义子类实现Callable接口;
子类中重写Callable接口中call()方法;
通过FutureTask类的构造器,将Callable接口的子类对象作为实际参数传递给FutureTask类的构造器中;
通过Thread类的构造器,将FutureTask类对象作为实际参数传递给Thread类的构造器中;
通过线程的start()方法,启动线程,回调call()方法。
方式四、线程池使用
创建ExecutorService接口实现类的一个对象,即具体的线程池对象;
调用execute()方法将Runnable接口实现类对象传入参数,执行run()方法;
调用submit()方法将Callable接口实现类对象传入参数,执行call()方法;
调用shutdown()方法,关闭线程池。
2.3 创建线程案例
-
准备工作
需求描述:计算大于给定数字num的最小质数
// 工具方法:判断是否是质数
private Boolean isPrime(long number) {
for (int i = 2; i * i <= number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
-
继承Thread类方式
class PrimeThread extends Thread {
private long minPrime;
public PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
@Override
public void run() {
// compute primes larger than minPrime
long temp = minPrime + 1;
while (!isPrime(temp)) {
temp++;
}
System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",初始结果:" + minPrime + ",结果数据:" + temp);
}
}
// 启动线程
PrimeThread p1 = new PrimeThread(21);
//①启动当前线程;②调用当前线程run()方法
p1.start();
-
实现Runnable接口
class PrimeRun implements Runnable {
private long minPrime;
public PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
@Override
public void run() {
// compute primes larger than minPrime
long temp = minPrime + 1;
while (!isPrime(temp)) {
temp++;
}
System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",初始结果:" + minPrime + ",结果数据:" + temp);
}
}
// 启动线程
PrimeRun p1 = new PrimeRun(11);
new Thread(p1).start();
-
实现Callable接口
class PrimeCall implements Callable<Long> {
private long minPrime;
public PrimeCall(long minPrime) {
this.minPrime = minPrime;
}
@Override
public Long call() throws Exception {
long temp = minPrime + 1;
while (!isPrime(temp)) {
temp++;
}
System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",初始结果:" + minPrime + ",结果数据:" + temp);
return temp;
}
}
// 启动线程
PrimeCall primeCall1 = new PrimeCall(345);
FutureTask<Long> futureTask = new FutureTask<>(primeCall1);
Thread t1 = new Thread(futureTask);
t1.start();
-
线程池方式
// 启动线程
ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(new PrimeRun(234));
service.submit(new PrimeCall(32));
service.shutdown();
2.4 Thread常用方法
-
Thread的常用方法
public synchronized void start() // 启动线程,执行run()方法
public void run() // 线程调度时执行的具体操作
public void setName(String name) // 设置该线程名称
public String getName() // 返回线程的名称
public static native Thread currentThread() // 获取当前线程对象,相当于this
public static native void yield() // 线程让步,暂停当前正在执行线程,将执行机会让给优先级相同或者更高的线程
public final void join() throws InterruptedException
// 在程序的执行过程中,调用某个某个线程的join()方法,则调用线程阻塞,直到join()方法所在线程执行结束
// 例如Main线程中调用t1线程,调用t1.join(),则等待t1线程执行结束之后,继续执行Main线程
public static native void sleep(long millis) throws InterruptedException
// 令当前线程在指定时间内放弃CPU的控制权(指定时间:毫秒)
public final native boolean isAlive() // 判断线程是否还活着
-
线程的优先级
// 线程的优先级等级
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
// 线程优先级涉及的方法
public final void setPriority(int newPriority) // 改变线程的优先级
public final int getPriority() // 线程优先值
说明:
线程创建时继承父线程的优先级;
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用;
调度策略,高优先级使用优先调度的抢占式策略,同级优先级使用先进先出的策略。