这篇文章主要介绍“实现Java线程的方法有哪些”,在日常操作中,相信很多人在实现Java线程的方法有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”实现Java线程的方法有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
搞懂两件事: 为什么说本质上只有一种实现线程的方式? 实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?
public class RunnableThread implements Runnable{ @Override public void run() { System.out.println("用实现Runnable接口实现线程"); } public static void main(String[] args) { Thread thread=new Thread(new RunnableThread()); thread.start(); } }
如代码所示,首先通过 RunnableThread 类实现 Runnable 接口,然后重写 run() 方法,之后只需要把这个实现了 run() 方法的实例传到 Thread 类中就可以实现多线程。
public class ExtendsThread extends Thread{ @Override public void run() { System.out.println("用继承Thread类实现线程"); } }
与第 1 种方式不同的是它没有实现接口,而是继承 Thread 类,并重写了其中的 run() 方法。
下面是线程池中的源码,来看看线程池是怎么实现线程的:
/** * The default thread factory */ static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
对于线程池而言,本质上是通过线程工厂创建线程的,默认采用 DefaultThreadFactory ,它会给线程池创建的线程设置一些默认值,比如:线程的名字、是否是守护线程,以及线程的优先级等。但是最终都是通过new Thread()创建线程的,只是参数多了些而已。
public class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { return new Random().nextInt(); } public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); Future<Integer> future = executorService.submit(new CallableTask()); System.out.println(future.get()); System.out.println(future.isDone()); System.out.println(future.isCancelled()); } }
Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,如代码所示,实现了 Callable 接口,并且给它的泛型设置成 Integer,然后它会返回一个随机数。
但是,无论是 Callable 还是 FutureTask,它们首先和 Runnable 一样,都是一个任务,是需要被执行的,而不是说它们本身就是线程。它们可以放到线程池中执行,如代码所示, submit() 方法把任务放到线程池中,并由线程池创建线程,不管用什么方法,最终都是靠线程来执行的,而子线程的创建方式仍脱离不了最开始讲的两种基本方式,也就是实现 Runnable 接口和继承 Thread 类。
public static void innerClassThread(){ new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部类创建线程"); } }).start(); } public static void lambdaThread(){ new Thread(()->{ System.out.println("lambda表达式创建线程"); }).start(); }
实际上,匿名内部类或 lambda 表达式创建线程,它们仅仅是在语法层面上实现了线程,并不能把它归结于实现多线程的方式,如匿名内部类实现线程的代码所示,它仅仅是用一个匿名内部类把需要传入的 Runnable 给实例出来。
了解了上面的几种创建线程的方式后,我们发现所有其他创建线程的方式都仅仅是在 new Thread() 外做了一层封装而已;不同点仅仅在于实现线程运行内容的不同。
实现线程执行的内容,通过实现 Runnable 接口的方式
启动线程需要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,Thread中的run()方法源码如下:
/* What will be run. */ private Runnable target; @Override public void run() { if (target != null) { target.run(); } }
target其实就是一个Runnable,实际线程执行的内容就是实现了Runnable的接口中的run方法。
实现线程执行的内容,继承 Thread 类重写 run() 方法的方式
启动线程需要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,但是此时的run()方法,因为继承的原因,此时已被子类重写了,所以此时线程执行的内容是子类的run方法。
运行内容主要来自于两个地方,要么来自于 target,要么来自于子类重写的 run() 方法;因此可以这样总结:本质上,实现线程只有一种方式new Thread(),而要想实现线程执行的内容,却有两种方式,要么通过实现 Runnable 接口的方式,要么继承 Thread 类重写 run() 方法的方式,把我们想要执行的代码传入,让线程去执行。
Runnable 里只有一个 run() 方法,它定义了需要执行的内容,在这种情况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,职责分明。
java单继承的原因,类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,限制了代码未来的可拓展性。
在某些情况下可以提高性能,如果使用继承 Thread 类方式,每次执行一次任务,都需要新建一个独立的线程;使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。
到此,关于“实现Java线程的方法有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。