线程八大核心+Java并发底层原理精讲 Java并发核心知识体系精讲 第三章 学习笔记

JAVA学习网 2020-08-12 07:59:08

线程八大核心+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

 

 

 

 

阅读(2357) 评论(0)