java实现多线程的方式有三种:
1、继承Thread类,重写该类的run方法(Thread类实现了Runnable接口)
public class TestThread extends Thread{
/**
* @param args
*/
public static void main(String[] args) {
TestThread thread = new TestThread();
thread.start();
}
public void run(){
System.out.print("Thread is running");
}
}
2、实现Runnable接口,实现接口中的run方法
public class TestThread implements Runnable{
/**
* @param args
*/
public static void main(String[] args) {
Thread thread = new Thread(new TestThread());
thread.start();
}
public void run(){
System.out.print("Thread is running");
}
}
3、实现Callable接口,实现call方法
public class TestThread implements Callable{
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask task = new FutureTask(new TestThread());
Thread thread = new Thread(task);
thread.start();
System.out.print(task.get());
}
public String call(){
return "Thread is running";
}
}
三者有何异同呢?我们来逐一比较:
1、首先我们都知道java类只能继承一个父类,但可以实现多个接口,因此对比继承Thread类,实现Runnable方法的同时还能继承我们需要的父类,显然这种方式更具有拓展性,且我们可以通过实现Runnable将线程任务的封装,交由多个线程一起执行
2、实现Callable方法相比于前两种方式,他需要实现call方法,且该方法有返回值,适合需要线程执行完之后返回结果的情况。例如我们有时候主线程想要做某一步操作,但是不必马上知道这个操作的结果,也不想在这个操作上进行等待,那可以new一个新线程,然后实现callable接口,让一个异步子线程去完成这个操作,然后主线程可以继续去做其他事情,等到需要用到这个操作返回结果的时候,就可以用fatureTaskd的get方法获取返回值。值得注意的是,如果异步子线程暂未执行完毕,get方法会一直阻塞直到拿到返回结果,当然,我们也可以选择使用get(long timeout,TimeUnit unit)来实现超时等待,若超过等待时间则不再阻塞,直接返回;future在NIO实现的netty中有广泛应用
3、实现callable接口的线程任务是可以通过futureTask的cancel方法进行取消的
那么,我们在什么时候需要用到多线程呢?
1、在硬件条件足够好的情况下,使用多线程增加任务处理速度;例如多核CPU系统,批量处理系统利用多线程加快任务处理速度;
2、在许多IO等待的场景下,若使用单线程则当IO阻塞时线程无法处理其他事物,导致卡死,或者线程出现异常中断后,导致功能不可用,此时也可以使用多线程
3、同步转异步场景下,我们需要使用一个子线程去进行某种操作,主线程继续往下执行或直接返回,减少响应等待的场景
是不是多线程一定比单线程好呢?
1、在单核CPU的物理机上,多线程上下文之间的频繁切换是很耗费cpu资源的,此时单线程是要比多线程快的;
2、线程的频繁创建、销毁也是消耗CPU性能和内存的,因此一般采用线程池管理