多线程
进程: 操作系统(OS)并发的一个任务
并发原理: CPU分时间片, 多个任务交替执行 宏观并行, 微观串行. 由OS调度
线程: 一个进程中并发的一个顺序执行流程
线程的组成:
- CPU
- 数据空间 堆空间共享, 栈空间独立 (每个线程都会有自己的栈空间 , 多个线程共享同一个堆空间)
- 代码
-
实现Runnable接口. 实现 run() 方法
class Task implements Runnable{ public void run() { for(int i = 1 ; i <= 10000 ; i++) { System.out.println("AAA "+i); } } } Runnable task = new Task(); // 任务 Thread t1 = new Thread(task); // 创建线程对象 t1.start();
-
继承Thread类, 覆盖run()方法
class MyThread extends Thread{ public void run() { for(int i = 1 ; i <= 10000 ; i++) { System.out.println("BBB "+i); } } } Thread t2 = new MyThread(); t2.start();
线程的状态
线程池 (Thread Pool) JDK5
当线程的任务结束, 线程不终止, 避免反复创建和销毁线程, 重用线程资源, 提升效率
可以将多个任务分配给一个线程,线程结束任务A , 还可以去执行任务B
对于固定长度的线程池, 可以控制最大并发数
Runnable task1 = new TaskA();
Runnable task2 = new TaskB();
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(task1);
es.submit(task2);
es.shutdown();
Callable接口 取代Runnable 有返回值, 可以抛异常
Callable<Integer> task1 = new TaskA();
Callable<Integer> task2 = new TaskB();
ExecutorService es = Executors.newCachedThreadPool();
Future<Integer> f1 = es.submit(task1);
Future<Integer> f2 = es.submit(task2);
Integer result1 = f1.get();
Integer result2 = f2.get();
System.out.println(result1 + result2);
线程的同步
多线程访问同一个对象 (临界资源) , 如果破坏了原子操作, 就会导致数据不一致
synchronized (obj) { // 对 obj 对象加锁的同步代码块
}
每个对象都有一个互斥锁 (monitor), 用来分配给线程
线程只有拿到obj的锁,才能进入对obj加锁的同步代码块
线程离开同步代码块, 会释放对应的锁
public synchronized void m(){} // 同步方法 方法实现: 对this加锁的同步代码块
线程只有拿到obj的锁,才能调用obj的同步方法
线程结束对obj同步方法的调用, 会释放obj的锁
ArrayList 线程不安全 并发效率高
Vector 线程安全 所有方法都是同步方法 并发效率低 1.0 (淘汰)
线程安全的集合类 JDK5
-
CopyOnWriteArrayList List
不修改原有数组, 通过复制新数组的方式完成List中的修改操作. 查询场景远多于修改场景
-
ConcurrentHashMap Map
JDK5 - JDK7 分段锁 把Map分为16个片段, 每个片段独立加锁
JDK8 CAS(Compare And Swap) 比较交换算法 无锁算法
-
CopyOnWriteArraySet Set
-
ConcurrentLinkedQueue Queue 队列 (FIFO)
CAS 无锁算法, 实现线程安全
-
接口 BlockingQueue (Queue的子接口 当使用队列的条件不满足时自动阻塞)
-
ArrayBlockingQueue
数组实现 有界阻塞队列 数组满时添加元素的线程会阻塞;数组空时删除元素的线程会阻塞
-
LinkedBlockingQueue
链表实现 无界阻塞队列 链表为空时删除元素的线程会阻塞
-
死锁 - 等待通知机制
synchronized(o1){
t1
synchronized(o2){
}
}
synchronized(o2){
t2
synchronized(o1){
}
}
-
wait() 来自Object obj.wait() 必须放在对obj加锁的同步代码块 . 执行结果:线程释放锁标记, 进入阻塞
-
notify() / notifyAll() 来自Object obj.notify()/obj.notifyAll() 必须放在对obj加锁的同步代码块
执行结果: 通知对obj阻塞的 一个/全部 线程可以离开阻塞状态 线程不会释放锁标记
wait() 和 sleep() 有什么区别 ? wait()释放锁标记, sleep()不释放锁标记
单例模式
设计一个类 , 这个类只能有一个对象
饿汉式实现 缺点: 无论对象是否需要, 都先把对象创建好. 可能浪费空间
class ClassA{
private static ClassA instance = new ClassA();
public static ClassA newInstance() {
return instance;
}
private ClassA() {}
}
懒汉式实现 缺点: 并发效率低
class ClassB{
private static ClassB instance = null;
public static synchronized ClassB newInstance() {
if (instance == null) instance = new ClassB();
return instance;
}
private ClassB() {}
}
class ClassC{
private static class Holder{
static ClassC instance = new ClassC();
}
public static ClassC newInstance() {
return Holder.instance;
}
private ClassC() {}
}