为什么需要多线程?
- CPU/内存/IO巨大的性能差异;
- 多核CPU的发展;
- 多线程可以使得多个执行流,并发执行。
多线程的缺点?
- 占用资源:每个线程有独立的方法栈(方法栈里保存有局部变量表和操作数栈);
- 上下文切换。
Thread底层模型?
- Thread在Linux称为轻量级进程,和进程并没有本质的区别,都有自己的PID;唯一的区别就是一个进程内部的多个线程共享一个JVM内存,而多个线程之间内存独立,互不干扰。
- 优点:1、简单,直接依赖操作系统的调度器;
- 缺点:1、占用资源多,每个线程都有独立的方法栈;2、上下文切换;3、不灵活,不能实现灵活的优先级(因为依赖操作系统的调度器)。
Thread的生命周期?
-
new、runnale、terminated、blocked、waiting、time waiting
Thread生命周期
ThreadLocal
协程
Runnable/Callable:
- 都表示一个要被线程执行的任务
- Runnable不能有返回值,不能抛出异常,Callable增加了这两个功能。
public interface Runnable {
public abstract void run();
}
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Java Memory Model
- 方法中的局部变量是线程私有的(存在方法栈,栈只存基本数据类型和堆中对象的引用, String不是基本数据类型);
-
除此以外其他东西都是线程共享的。
JMM
什么是线程安全问题?
一个类在多线程环境下使用仍能表现出正常的行为
解决线程安全问题的办法:
- 不可变对象;
- 各种锁;
- 并发工具包(底层通常是CAS)compare and swap:
Boolean -> AtomicBoolean; int / long -> AtomicInteger/AtomicLong; [ ]->AtomicLongArray; Object->AtomicReference; HashMap -> ConcurrentHashMap; ArraysList -> CopyOnWriteArrayList; TreeMap->ConcurrentSkipListMap
手写一个死锁
public class DeadLock {
static Object lock1 = new Object();
static Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException {
Thread newThread1 = new Thread(() -> {
synchronized (lock1) {
try {
Thread.sleep(10000);
synchronized (lock2) {
System.out.println(" lock2 ");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
newThread1.start();
synchronized (lock2) {
Thread.sleep(10000);
synchronized (lock1) {
System.out.println(" lock1 ");
}
}
}
}
volatile
- 保证可见性,并非原子性;写入volatile变量会直接写入主内存,读取volatile变量会直接读取主内存。(是一个非常弱的同步机制)
- 禁止指令重排:编译器和处理器都可能对指令进行重排,导致出现问题。
- 有同步的时候不需要volatile:synchronized, Lock, AtomicInteger
网友评论