多线程
1.区分并发与并行
并发:单核CPU在某一个时间段,不断的交互运行,宏观并行,微观并发。
并行:多核CPU,在同一时刻,同时运行多个应用程序
2.区分进程与线程
进程:系统运行的某一个应用
线程:进程中处理的多个任务,依赖进程存在,一个进程可以有多个线程
3.线程的创建
创建线程的四种方式:继承Thread类,实现Reunable接口,实现Callable接口,使用线程池Exceutor。
1.实现Runnable接口
实现run方法 Runnable对象:任务对象 创建线程对象:new Thread(任务对象)
2.继承Thread类
覆盖run方法
3.创建线程池(避免重复的创建和销毁线程,可以让一个线程执行多个任务)
ExecutorService es = Executors.newFixedThreadPool(线程sum);(固定数量线程池)
ExecutorService es = Executors.newCachedThreadPool();(可变线程池)
es.submit(任务对象);
es.shutdown();关闭线程池
4.实现Callable接口(有返回值,能抛异常)
ExecutorService es = Executors.newCachedThreadPool();
实现call方法 Callable对象:任务对象 创建线程对象:es.submit(任务对象);
接收返回值并跑异常:Future f1 = es.submit(任务对象);
4.线程的组成
CPU时间片:操作系统会给每个线程分配执行时间
运行数据:
堆空间(共享):存储线程需使用的对象,多个线程可以共享堆种的对象
栈空间(独立):存储线程需要使用的局部变量,每个线程都拥有独立的栈
线程的逻辑代码
5.Thread的基本状态
初始状态:线程对象被创建,只在堆中开辟内存
就绪状态:调用start()之后,进入就绪状态,等待系统选中,并分配时间片
运行状态:获得时间片之后,进入运行状态,如果时间片到期,回到就绪状态
终止状态:主线程main()或独立线程run()结束,进入终止状态并释放拥有的时间片
等待状态:
有限期等待
1.调用Thread.sleep()进入有限期等待状态,到期后线程进入就绪状态,等待系统分配时间片,不会释放锁标记
无限期等待
2.调用线程.join()进入无限期等待,
阻塞状态:线程没有锁标记进入阻塞状态 ,等待锁标记
6.Thread中的方法
sleep:限时等待 休眠
yield:放弃CPU 回到就绪状态
setDaemon(true):设置线程为守护线程,所有非守护线程都结束时,进程就会结束
join():当前线程进入等待状态,直到线程终止,才会恢复执行
7.线程安全问题
当多线程共同访问同一个对象(临界资源)的时候,如果破坏了不可分割的操作(原子操作),就可能发生数据不一致
每个Java对象都有一个互斥锁标记,用来分配给线程
synchronized(o) { } 对 o 加锁的同步代码块,只有拿到o的锁标记的线程,才能进入对o枷锁的同步代码块
synchronized 方法修饰符 对this加锁的同步代码块 只有拿到o的锁标记的线程,才能调用o的同步方法
8.死锁问题
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
避免死锁: 加锁顺序(线程按照一定的顺序加锁),锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁),死锁检测,避免嵌套锁
9.线程通信(等待通知机制)
object.wait():必须出现在对object加锁的同步代码块 线程会释放锁标记,进入等待状态
object.notify/notifyAll():必须出现在对object加锁的同步代码块,从等待状态中释放一个/全部线程
10.Lock接口
创建Lock对象
Lock lock = new ReentrantLock();
上锁:调用lock.lock()方法
解锁:调用lock.unlock()方法
获得队列对象
Condition c1 = lock.newCondition();
c1.await(): 进入等待队列
c1.signalAll():唤醒等待的线程
11.jdk1.5线程安全的集合
用ArrayList实现一个高并发读写分离的集合
package com.baizhi.controller;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by 拂晓 on 2019/9/15:18:01
*/
public class MyList extends ArrayList {
//读写锁 写锁未分配时,读锁可以反复分配 读写分离
ReadWriteLock rwl = new ReentrantReadWriteLock();
Lock r1 = rwl.readLock();//共享锁
Lock w1 = rwl.writeLock();
@Override
public int size() {
try {
r1.lock();
return super.size();
}finally {
r1.unlock();
}
}
@Override
public Object get(int index) {
try {
r1.lock();
return super.get(index);
}
finally {
r1.unlock();
}
}
@Override
public boolean add(Object o) {
try {
w1.lock();
return super.add(o);
}finally {
w1.unlock();
}
}
@Override
public Object remove(int index) {
try {
w1.lock();
return super.remove(index);
}finally {
w1.unlock();
}
}
@Override
public void clear() {
try {
w1.lock();
super.clear();
}finally {
w1.unlock();
}
}
}
以下线程安全的集合类,都来自Java并发包java.util.concurrent
CopyOnWriteArrayList : 利用复制数组的方式实现数组元素的修改 写效率低,读效率高 (适用于读操作多于写操作的场景),同样的还有CopyOnWriteArraySet类
ConcurrentHashMap:实现方式是分段锁,将散列表分割成16段,每段加锁,各段互不影响,读写效率高
ConcurrentLinkedQueue:线程安全的队列,用链表实现的队列,实现方式无锁算法CAS(比较交换算法),效率很高
12.阻塞队列(BlockingQueue)
BlockingQueue是Queue的子接口
实现类:
ArrayBlockingQueue 数组实现 可以实现有界队列 put方法会发生等待 需要指定容量,不会自动扩充
LinkedBlockingQueue 链表实现 可以实现无界队列 put方法不会发生等待
put(): 添加元素到队列中 如果队列满,则等待
take(): 删除队列头元素,如果队列空,则等待
13.集合的整理
Collection
|-List(ArrayList LinkedList Vector CopyOnWriteArrayList)
|-Set(HashSet LinkedHashSet CopyOnWriteArraySet)
子接口|-SortedSet ( TreeSet)
|-Queue (LinkedList ConcurrentLinkedQueue)
子接口|-BlockingQueue(ArrayBlockingQueue LinkedBlockingQueue )
Map ( HashMap LinkedHashMap Hashtable Properties ConcurrentHashMap)
子接口|-SortedMap (TreeMap)