Java 开启线程的四种方式
实现runnable接口
这个方法有一个很大的缺点就是重写的run方法是没有返回值的,如果想要返回值,需要使用下面的方法
public class RunnableImpl implements Runnable {
/*
* 创建步骤如下:
* 1,定义Runnable接口的实现类,并且实现run方法,这个方法同样是线程执行体
* 2,创建Runnable实现类的实例,并以此实例对象作为Thread的target来创建Thread类,这个新创建的Thread对象才是真正的线程对象,即开启了新的线程
* 3,调用线程对象的start()方法来开启该线程
*
* 调用示例:
* //开启10个线程
* for (int i = 0; i < 10; i++) {
* Thread thread = new Thread(new RunnableImpl());
* thread.start();
* }
* */
/**
* 实现Runnable接口的run方法,这个方法称为线程执行体
* */
@Override
public void run() {
doSomething();
}
/**
* 需要处理的任务
* */
private void doSomething(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i);
}
}
}
实现callable接口
实现callable接口的类的实例对象不能直接当做线程执行体传递给thread,
而是需要先用futuretask包裹,从实现callable转为实现runnable,
再把task传递给thread构造线程。
这种方法可以获得方法执行的返回值,并且可以自定义返回类型,返回值被task接收,
还可以抛出异常,
public class CallableImpl implements Callable<String> {
/*
* 创建步骤如下:
* 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
* 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
* 3,将FutureTask的实例,作为Thread的target来创建Thread类
* 4,调用start方法,开启线程
*
* 调用示例:
* Callable<String> tc = new CallableImpl();
* FutureTask<String> task = new FutureTask<>(tc);
* new Thread(task).start();
* try {
* System.out.println(task.get());
* } catch (InterruptedException | ExecutionException e) {
* e.printStackTrace();
* }
*
* 说明:
* 1.与使用Runnable相比, Callable功能更强大些
* 2.实现的call()方法相比run()方法,可以返回值
* 3.方法可以抛出异常
* 4.支持泛型的返回值
* 5.需要借助FutureTask类,比如获取返回结果
* Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
* FutureTask是Futrue接口的唯一的实现类
* FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
*
* */
private int ticket = 5;
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(doSomething());
}
return "出票任务完成";
}
public String doSomething() {
String result = "";
if (this.ticket > 0) {
result = "出票成功,ticket=" + this.ticket--;
} else {
result = "出票失败,ticket=" + this.ticket;
}
return result;
}
}
继承thread类
public class ExtendThread extends Thread {
/*
* 创建步骤如下:
* 1,定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run方法称为线程执行体。
* 2,创建Thread子类的实例,即创建线程对象。本实例中是new一个ExtendThread,即可创建线程对象,也就是开启了一个线程
* 3,调用线程对象的start()方法来启动该线程。
*
* 调用示例:
* //循环10次即开启10个线程
* for (int i = 0; i < 10; i++) {
* ExtendThread extendThread = new ExtendThread();
* extendThread.start();
* }
* */
/**
* 重写Thread类的run(),这个方法称为线程执行体
* */
@Override
public void run() {
doSomething();
}
/**
* 需要处理的任务
* */
public void doSomething(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i);
}
}
}
创建线程池
public class ThreadPool implements Runnable {
/*
* 创建步骤如下:
* 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
* 2,创建一个自定义线程个数的线程池
* 3,实例化Runnable接口的实现类
* 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
* 5,关闭线程池
*
* 调用示例:
* ExecutorService pool = Executors.newFixedThreadPool(2);
* ThreadPool threadPool = new ThreadPool("AA");
* ThreadPool threadPoo2 = new ThreadPool("BB");
* pool.execute(threadPool);
* pool.execute(threadPoo2);
* pool.shutdown();
*
* 说明:
* 示例中创建的是2个线程的线程池
* execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程,因为thread类实现了runnable接口
*
* */
String name;
public ThreadPool(String name) {
this.name = name;
}
@Override
public void run() {
doSomething();
}
/**
* 需要处理的任务
* */
private void doSomething() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i + ",name=" + this.name);
}
}
}
匿名内部类
public class Anonymous {
/*
* 创建步骤如下:
* 匿名内部类本质上也是一个类实现了Runnable接口,重写了run方法,只不过这个类没有名字,直接作为参数传入Thread类
*
* 调用示例:
* //开启10个线程
* for (int i = 0; i < 10; i++) {
* Anonymous anonymous =new Anonymous();
* anonymous.myRun();
* }
*
* */
public void myRun(){
new Thread(new Runnable() {
@Override
public void run() {
doSomething();
}
}).start();
}
/**
* 需要处理的任务
* */
private void doSomething(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i);
}
}
}
在Java中,A线程调用B线程做某些工作,对于B的结果,A线程只想等待特定的时间而非一直等待,应该怎么做?
在Java中,你可以使用Thread.join()
方法来等待一个线程执行完成,但是这会一直等待直到被等待的线程执行完成为止。如果你希望在等待一段特定的时间后不再等待,你可以结合使用Thread.join()
和TimeUnit
类。以下是一个示例:
public class Main {
public static void main(String[] args) {
// 创建B线程并启动
Thread bThread = new Thread(() -> {
// B线程执行一些工作
// ...
// 假设B线程执行完成后设置了一些结果
System.out.println("B线程执行完成");
});
bThread.start();
// A线程等待B线程执行一段时间
try {
long timeoutMillis = 5000; // 设置等待时间为5秒
bThread.join(timeoutMillis);
if (bThread.isAlive()) {
// 如果B线程在超时时间内仍然没有执行完成
System.out.println("B线程还未执行完成,超时退出");
} else {
// 如果B线程在超时时间内执行完成
System.out.println("A线程继续执行,B线程执行完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在上面的例子中,bThread.join(timeoutMillis)
会等待B线程最多执行timeoutMillis
毫秒的时间。如果B线程在超时时间内执行完成,那么A线程会继续执行,否则A线程会在超时后不再等待B线程而继续执行自己的逻辑。