Java学习-多线程-01

Java 学习-多线程-01

1.线程的生命周期、线程有几种状态

生命周期:

  1. 初始化(NEW)
  2. 运行(RUNNABLE)
  3. 阻塞(BLOCKED)
  4. 等待(WAITING)
  5. 有时限等待(TIMED_WAITING)
  6. 死亡(TERMINATED)

线程状态:
有 6 个状态:

1
2
3
4
5
6
7
8
9
// Thread.State 源码
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

因为阻塞(BLOCKED)、等待(WAITING)、有时限等待(TIMED_WAITING)三种状态都是阻塞状态,没有 CPU 的使用权,所以可以用下图表示:


2.sleep()、wait()、join()、yield()区别


sleep

  • sleep 方法是 Thread 的静态方法
  • sleep 使当前线程进入阻塞状态,在指定时间内不会执行
  • sleep 期间,不会释放锁

wait

  • wait 方法是 Object 的非静态方法
  • 在其他线程调用 notify 或者 notifyAll 方法前,线程释放当前所占用的锁标识
  • 当前线程必须拥有当前对象锁
  • wait 和 notify 必须在 synchronized 代码块中使用

yield

  • 暂停当前线程对象

join

  • join 方法是 Thread 的静态方法
  • 等待调用 join 方法的线程结束,在继续执行

3.对线程安全的理解

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象时线程安全的。


4.Thread、Runable 的区别

  • Thread 的实现方式是继承
  • Runable 的实现方式是实现接口
  • Thread 实现了 Runable 接口,并进行了扩展

5.对守护线程的理解

守护线程是运行在后台的一种特殊进程。它独立于控制台,并且不会影响控制台的状态。守护线程的主要作用是负责系统的内存管理,以及系统的安全措施。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 守护线程
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("守护线程");
}
}
});
thread.setDaemon(true);
thread.start();
}
}

6.ThreadLocal 的原理和使用场景

ThreadLocal 是什么

ThreadLocal 的作用是给线程提供一个 作用域是整个线程,生命周期是线程存活时期的 **线程局部变量**。每个线程都可以通过 ThreadLocal 对象来访问属于自己的数据。总的来说有以下两个特点:

  1. 线程内共享,线程运行到那里了都可以使用 ThreadLocal 中存储的变量
  2. 线程间隔离,线程只能看到自己存储在 ThreadLocal 中的变量,其他线程不能访问
ThreadLocal 的原理

ThreadLocal 的原理是:在线程中存储一个 ThreadLocal 对象,每个线程都有自己的 ThreadLocal 对象,每个线程都有自己的 ThreadLocal 对象中的变量。

void set(T value)
set 方法设置当前线程中 threadLocal 变量的值,该方法的源码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void set(T value) {
//1. 获取当前线程实例对象
Thread t = Thread.currentThread();

//2. 通过当前线程实例获取到ThreadLocalMap对象
ThreadLocalMap map = getMap(t);

if (map != null)
//3. 如果Map不为null,则以当前threadLocl实例为key,值为value进行存入
map.set(this, value);
else
//4.map为null,则新建ThreadLocalMap并存入value
createMap(t, value);
}

value 值被存放在 ThreadLocalMap 里,由每个 Thread 各自维护,每个 Thread 都有自己的 ThreadLocalMap,每个 Thread 都有自己的 ThreadLocalMap 中的变量。

ThreadLocal 的使用场景
  1. ThreadLocal 不是用来解决共享对象的多线程访问问题。
  2. ThreadLocal 是让每个不同的线程拥有属于自己的数据容器( ** ThreadLocalMap ** )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ThreadLocalDemo {
private static ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<>();

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.submit(new DateUtil("2019-11-25 09:00:" + i % 60));
}
}

static class DateUtil implements Runnable {
private String date;

public DateUtil(String date) {
this.date = date;
}

@Override
public void run() {
if (sdf.get() == null) {
sdf.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
} else {
try {
Date date = sdf.get().parse(this.date);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
}

7.ThreadLocal内存泄漏的原因,如何避免

内存泄漏的原因

ThreadLocal 数据实际上是存放在其内部类 ThreadLocalMap里面。 ThreadLocal 的get、set、remove方法,实际上是调用 ThreadLocalMap 的 getEntry、setEntry、removeEntry 方法。

ThreadLocalMap内部通过一个Entry类型的table数组来维护数据。

Entry中的key是弱引用,当threadLocal外部强引用被置为null(threadLocalInstance=null),那么系统 GC 的时候,根据可达性分析,这个threadLocal实例就没有任何一条链路能够引用到它,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。

避免

每次使用完线程数据,都要调用一次ThreadLocal.remove()方法手动删除Entry对象


8.并发、并行、串行的区别

  • 串行:时间上不可重叠发生,前一个任务还没完成,下一个任务只能等待;
  • 并行:时间上可以重叠,多个任务在同一时刻互补干扰的同时执行;
  • 并发:多个任务在同一时期内同时发生执行;

9.并发的三大特性

  • 原子性:
    原子性值指一个操作是不可分割、不可中断的要么全部执行并且执行的过程不会被任何因素打断,要么全部不执行。
  • 可见性:
    可见性指的是一个线程修改了某一个共享变量时,其他线程能够立即知道这个修改。
  • 有序性:
    有序性指的是一个线程的执行代码,从前往后执行,单线程下可以认为是有序的,但是并发时可能发生指令重排。

10.volatile

  • 保证变量的内存可见性
  • 禁止volaite变量与其他变量的指令重排序

PS:重排序:为优化程序性能,对原有的指令执行顺序进行优化重新排序。重排序可能发生在多个阶段,比如编译重排序、CPU重排序等。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!