线程八大核心+Java并发底层原理精讲 Java并发核心知识体系精讲 第三章 学习笔记
第3章 核心1:实现多线程的正确姿势【解读官方文档】
相信很多小伙伴经常在各大技术博客或者论坛甚至面试中、工作中迷茫于线程到底有几种实现方式。真如网络所说实现线程有N种方式么?
本章节利用【官方文档】现身说法,足够权威,让你即使面对面试官也能从容不迫。
3-1 究竟如何创建新线程?创建线程的方式2种(来自Oracle官方文档)
* There are two ways to create a new thread of execution. One is to * declare a class to be a subclass of <code>Thread</code>. This * subclass should override the <code>run</code> method of class * <code>Thread</code>. * The other way to create a thread is to declare a class that * implements the <code>Runnable</code> interface. 方法一:实现Runnable接口 方法二:继承Thread类 package threadcoreknowledge.createthreads; /** * 描述:用Runnable方式创建线程 */ public class RunnableStyle implements Runnable{ public static void main(String[] args) { Thread thread = new Thread(new RunnableStyle()); thread.start(); } @Override public void run() { System.out.println("用Runnable方法实现线程"); } } package threadcoreknowledge.createthreads; /** * 描述:用Thread方式实现线程 */ public class ThreadStyle extends Thread{ @Override public void run() { System.out.println("用Thread类实现线程"); } public static void main(String[] args) { ThreadStyle thread1 = new ThreadStyle(); thread1.start(); } } 3-2 同时使用两种方法和正确实现方法的总结
思考??实现Runnable接口和继承Thread类哪种方式更好?
方法1(实现Runnable接口)更好,方法2是不推荐的,因为它有以下的一些缺点:
1. 从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread 类)”解耦,用 runnable对象可以实现解耦。通过Runnable方式实现的run方法中的内容是具体执行的任务,可以让一个单独任务类实现runnable接口,然后把对应的实例传入Thread类就可以。这样的话,同样的一个任务类,可以传给不同的Thread,并且任务类也不负责创建线程等工作,是解耦的。
2. 使用继承Thread的方式的话,那么每次想新建一个任务,只能新建一个独立的线程, 而这样做的损耗会比较大(比如重头开始创建一个线程、执行完毕以后再销毁等。如果线程的实际工作内容,也就是run()函数里只是简单的打印一行文字的话,那么可能线程的实际工作内容还不如损耗来的大)。如果使用Runnable和线程池,就可以大大减小这样的损耗。
3. 继承Thread类以后,由于Java语言不支持双继承,这样就无法再继承其他的类,限制了可扩展性。 通常我们优先选择方法1。
方法2(继承Thread类)也有几个小小的好处,但相比于缺点来说,优点都不值一提,比如:
1. 在run()方法内获取当前线程可以直接用this,而无须用Thread.currentThread()方法;
2. 继承的类的内部变量不会直接共享,少数不需要共享变量的场景下使用起来会更方便。
两种方法的本质对比
方法一和方法二,也就是 “实现Runnable接口并传入Thread类”和“继承Thread类然后重写run()”在实现多线程的本质上,并没有区别,都是最终调用了start()方法来新建线程。这两个方法的最主要区别在于run()方法的内容来源:
@Override public void run() { if (target != null) { target.run(); } }
方法一:最终调用target.run();
方法二:run()整个都被重写
3-3 典型错误观点剖析
“线程池创建线程也算是一种新建线程的方式”
package threadcoreknowledge.createthreads.wrongways; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 描述:线程池创建线程的方法 */ public class ThreadPool5 { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { executorService.submit(new Task() { }); } } } class Task implements Runnable { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } }
“通过Callable和FutureTask创建线程,也算是一种新建线程的方式”
无返回值是实现runnable接口,有返回值是实现callable接口,所以callable是新的实现线的方式
import java.util.Timer; import java.util.TimerTask; /** * 描述:定时器创建线程 */ public class DemoTimmerTask { public static void main(String[] args) { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }, 1000, 1000); } }
“定时器” “匿名内部类”
package threadcoreknowledge.createthreads.wrongways; /** * 描述:匿名内部类的方式 */ public class AnonymousInnerClassDemo { public static void main(String[] args) { new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }.start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }).start(); } }
“Lambda表达式”
package threadcoreknowledge.createthreads.wrongways; /** * 描述:lambda表达式创建线程 */ public class Lambda { public static void main(String[] args) { new Thread(() -> System.out.println(Thread.currentThread().getName())).start(); } }
线程八大核心+Java并发底层原理精讲 Java并发核心知识体系精讲 学习笔记
下载地址:
百度网盘:
链接: https://pan.baidu.com/s/1WBBj3CeCEi0arO_eFHsNSQ 密码: g8q0
如果失效:联系微信:itit11223344