文章目录
基本概念
并发:宏观并行,微观交替执行
并行:在同一时刻处理进程
同步:进程间相互依赖,下个进程会等待上个进程的结果
互斥:进程间相互排斥的使用临界资源的现象
异步:进程彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。
多线程:多线程是程序设计的逻辑层概念,是进程中并发运行的一段代码。是实现异步的手段。
程序:一段静态的代码,一组指令的有序集合,它本身没有任何运行的含义。
进程:进程是系统进行资源分配(最小单位)和调度的一个独立单位。是程序的一次动态执行。
线程:线程是进程中执行运算的最小单位,执行处理机调度的基本单位。引入原因:减少程序并发执行时所付出的时空开销
死锁
多个进程竞争资源导致无限期阻塞、相互等待的一种状态
- 互斥(资源只允许一个进程持有)
- 占有并等待(进程已有资源,继续请求其他临界资源)
- 不可抢占(进程资源不会被其他进程抢占)
- 循环等待(多个线程形成环形等待关系)
饥饿
进程长时间等待不被执行
活锁
进程占有资源后都主动释放资源给其他进程使用
多线程优点:多线程容易调度,有效地实现并发性。对内存的开销比较小。创建线程比创建进程要快。
全局安全点
线程状态
Runnable接口
通过实现Runnable接口来创建线程
public interface Runnable {
public abstract void run();
}
Callable和Future创建线程
线程池创建线程
Thread类
通过继承Thread类来创建线程
public class Thread implements Runnable {
//默认构造方法,线程名为Thread-0
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
//线程组,Runnable接口,线程名,栈大小
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//线程名不能为空
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//获得生成当前线程的线程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
//通过安全管理器获得线程组
if (security != null) {
g = security.getThreadGroup();
}
//如果安全管理器未提供线程组则使用parent的线程组
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
//设置当前线程线程组、是否为守护线程、线程优先级
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
//继承父线程的ThreadLocal
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
}
成员变量
//当前线程名
private volatile String name;
//优先级
private int priority;
private Thread threadQ;
//指向操作系统支持的线程
private long eetop;
//是否为守护线程
private boolean daemon = false;
private Runnable target;
//当前线程所在线程组
private ThreadGroup group;
//线程栈大小
private long stackSize;
//线程序号
private static long threadSeqNumber;
//记录线程状态二进制表示:new=0,runnable=100,terminate=10
//block=10000000000,wait=10000,time_wait=100000
private volatile int threadStatus = 0;
主要方法
start()
线程由new状态转为runnable状态
public synchronized void start() {
//如果线程状态不为new
if (threadStatus != 0)
throw new IllegalThreadStateException();
//将当前线程加入线程组
group.add(this);
//启动标识
boolean started = false;
try {
start0();
started = true;
} finally {
try {
//如果启动失败则从线程组中移除
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
wait()
释放锁,进入ObjectMonitor对象监视器中的_waitSet队列,挂起线程,等待唤醒,使用wait和notify时需要持有对象锁
//wait()->wait(0)
//jdk1.8:wait(long,int)->nanos>0,timeout++
public final native void wait(long timeout) throws InterruptedException;
notify()
唤醒_waitSet队列的一个线程,同步块执行完成后才释放锁
notifyAll()
唤醒_waitSet队列中的所有线程进入_EntryList竞争资源,同步块执行完成才释放锁
sleep(long time)
线程不释放锁进入阻塞状态,到指定时间后进入runnable状态,等待cpu调度执行
isAlive()
判断当前线程是否存活
public final native boolean isAlive();
join()
使当前线程阻塞,直到调用join的线程执行完成,通过调用当前线程的wait方法实现
//实际相当于调用wait(0)
public final void join() throws InterruptedException {
join(0);
}
//底层调用wait方法
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
//如果当前线程存活
while (isAlive()) {
//执行线程等待,当当前线程执行完成会自动调用其notifyAll方法
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
//类似wait(long,int)方法将计算的millis结果传给方法join(long millis)
public final synchronized void join(long millis, int nanos){}
interrupt()
中断线程,设置中断标志位,阻塞时调用抛出异常
//isInterrupted 根据中断标志位判断线程是否中断
//interrupted 根据中断标志位判断线程是否中断,并清除中断状态
yield()
使当前线程让出cpu资源,进入runnable状态,等待调度
public static native void yield();
currentThread()
返回当前正在执行的线程对象的引用
public static native void currentThread();
ThreadGroup
线程组表示一个线程的集合。此外,线程组也可以包含其他线程组。允许线程访问关于其线程组的信息,但不允许访问关于其线程组的父线程组或任何其他线程组的信息。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
//持有的父线程组
private final ThreadGroup parent;
//当前线程组中线程数量
int nthreads;
//存放线程的数组
Thread threads[];
}
线程通信
PipedOutputStream
PipedInputStream
PipedReader
PipedWriter
Lock
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。
public interface Lock {
//获得锁。
void lock();
//获取锁定,竞争锁的过程可以响应中断,退出等待
void lockInterruptibly() throws InterruptedException;
//返回一个新Condition绑定到该Lock实例
Condition newCondition();
//可轮询锁,如果可用,则获取锁定,并立即返回值为true 。 如果锁不可用,则此方法将立即返回值为false
boolean tryLock();
//定时锁,如果在给定的等待时间内是空闲的,并且当前的线程尚未 interrupted,则获取该锁。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁。
void unlock();
}
Condition
Condition取代了对象监视器方法的使用。一个Condition实例本质上绑定到一个锁,可实现多路通知。
public interface Condition {
//导致当前线程等待直到发信号或interrupted
void await() throws InterruptedException;
//使当前线程等待直到发出信号或中断,或指定的等待时间过去
boolean await(long time, TimeUnit unit) throws InterruptedException;
//使当前线程等待直到发出信号或中断,或指定的等待时间过去。
long awaitNanos(long nanosTimeout) throws InterruptedException;
//使当前线程等待直到发出信号,不响应中断
void awaitUninterruptibly();
//使当前线程等待直到发出信号或中断,或者指定的最后期限过去
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待线程
void signal();
//唤醒所有等待线程
void signalAll();
}
ReentrantLock—可重入锁
synchronized功能扩展,jdk1.6后与synchronized性能差距不大
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
//构造方法,默认不公平
public ReentrantLock() {
sync = new NonfairSync();
}
//设置公平或非公平锁
//公平锁:先提出锁获取请求优先分配
//非公平锁:按随机、就近原则分配,默认为非公平锁,非公平锁执行效率更高。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//获取竞争队列中的线程数
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
if (p.thread != null)
++n;
}
return n;
}
//获得条件队列中的线程数
protected final int getWaitQueueLength() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION)
++n;
}
return n;
}
}
Semaphore
一个计数信号量,是多锁的扩展,本质上是使用AbstractedQueuedSynchronizer实现的一个共享锁。
public class Semaphore implements java.io.Serializable {
//持有的同步器
private final Sync sync;
//构造器中初始化同步器为公平还是非公平
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
//获得指定数量信号量,如果不足则阻塞线程
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
//释放指定数量信号量
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
}
ReadWriteLock接口
维护一对关联的锁 ,一个用于读操作,一个用于写操作
public interface ReadWriteLock {
//返回用于读取的锁
Lock readLock();
//返回用于写入的锁
Lock writeLock();
}
ReentrantReadWriteLock
可重入读写锁,共享锁(线程对读取操作共享),排他锁(线程对写入操作互斥)
注意事项
- 读锁获得Condition会抛出异常
- 只有读锁与读锁之间不阻塞
成员变量
/** 内部类提供的读取锁 */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** 内部类提供给的写入锁 */
private final ReentrantReadWriteLock.WriteLock writerLock;
/** 持有的同步器 */
final Sync sync;
内部类
Sync
读写锁中维护的同步器
abstract static class Sync extends AbstractQueuedSynchronizer {
//位偏移
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//最大锁数量65535
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//低16位,独占锁掩码
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//第一个读锁
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
//当前线程持有的读锁数
private transient ThreadLocalHoldCounter readHolds;
//前一个成功获取读锁的线程持有的读锁数
private transient HoldCounter cachedHoldCounter;
//线程持有的计数器
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
//当前读写锁中读锁的数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//当前读写锁中写锁的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
}
获得读锁过程
- 如果同步器state的写锁数量不为0并且独占线程不为当前线程则当前线程进入同步器的竞争队列
- 判断是否满足获得读锁条件
2.1. 读锁是否需要被阻塞(公平锁:竞争队列存在节点则返回false,非公平锁:第一个节点为写线程则返回false)
2.2. 读锁数量是否超过上限(65535)
2.3 CAS将同步器读锁数量+1 - 如果条件成立则设置当前线程持有读锁的数量 ,如果为第一个获取读锁的线程就设置firstReaderHoldCount否则设置ThreadLocal变量HoldCounter
- 如果条件不成立,则进入fullTryAcquireShared方法自旋再次尝试获得锁,再次失败进入同步器的竞争队列
//读锁尝试获得锁
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//如果写锁数量不为0(state的低16位)并且独占线程不为当前线程的返回-1获取读锁失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//获得读锁数量
int r = sharedCount(c);
//非公平锁判断竞争队列头节点为写锁则返回false
//公平锁判断竞争队列中是否存在节点,存在则返回false
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//CAS将读锁加1
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
//如果读锁为0,则设置第一个读为当前线程
//第一个读线程持有锁数量为1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//如果第一个读线程为当前线程则将持有锁数量+1
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
//rh未初始化或rh前一个获取读锁的线程的tid与当前线程不等
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
//自旋再次尝试获取锁
return fullTryAcquireShared(current);
}
获得写锁过程
- 判断当前同步器持有锁的数目
- 如果同步器持有锁
2.1. 同步器持有的是读锁或持有写锁的线程不为当前线程,返回false,进入竞争队列
2.2.如果超过最大锁数量则抛出异常
2.3 设置state写锁数,返回true,获得锁成功 - 如果同步器无锁
3.1. 判断写操作是否需要被阻塞(非公平锁:直接返回false,公平锁:如果竞争队列中存在节点则返回true
3.2. 执行CAS操作修改state写锁数
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
//当前同步器持有锁
if (c != 0) {
// 同步器持有的是读锁或持有写锁的线程不为当前线程
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//如果持有锁数量超过最大数量抛出异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
//如果当前同步器没持有锁
//不公平锁:writeShouldBlock返回false
//公平锁:如果队列中存在节点则返回true
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
释放读锁过程
- 修改当前线程持有读锁数量
- 自旋修改读锁数,返回 读锁数==0
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
//如果当前线程为第一个获得读锁的线程
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
//如果不为第一个则获得ThreadLocal中的计数器
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
//释放读锁并不影响其他线程继续读取
//修改读锁数用于给写锁判断同步器是否持有锁
return nextc == 0;
}
}
释放写锁过程
- 修改同步器持有写锁数
protected final boolean tryRelease(int releases) {
//判断独占访问权限线程是否为当前线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//因为只有一个线程在执行当前代码,所以无需CAS修改state
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
//如果写锁数为0则设置独占访问权限线程为null
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
StampedLock
jdk1.8之后引进的读写锁,不可重入,ReentrantReadWriteLock在读的情况不允许写操作,而StampedLock提供悲观读锁实现在读的过程中允许写操作。对于可能导致读的数据不一致,StampedLock提供了方法来验证读锁后是否有其他写锁发生。
private final StampedLock lock = new StampedLock();
void update(int i) {
//获取写锁
long stamp = lock.writeLock();
try {
this.i = i;
} finally {
lock.unlockWrite(stamp);
}
}
int get() {
//获取乐观读锁
long stamp = lock.tryOptimisticRead();
int value = i;
//验证读取过程是否出现写入
if (!lock.validate(stamp)) {
//出现写入则使用悲观读锁再次读取
stamp = lock.readLock();
try {
value = i;
} finally {
lock.unlockRead(stamp);
}
}
return value;
}