0 线程阻塞、挂起与睡眠
状态 | 说明 |
挂起 | 一般是主动的;不释放CPU,可能释放内存,放在外存;会释放锁 |
阻塞 | 一般是被动的;释放CPU,不释放内存;不会释放锁; |
休眠 | 一般是主动的;一般会释放CPU,也可能占着CPU不工作;不会释放锁; |
表 线程挂起、阻塞和睡眠的特点
1 锁的种类
图 java锁类别
悲观锁:总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁。
乐观锁:总是假设最好的情况,认为共享资源每次访问的时候不会出现问题,线程可以不停执行,无需加锁,只是在提交修改的时候去验证对应的资源是否被其他线程修改了。
共享锁:该锁可被多个线程锁持有。如果线程对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排他锁。获得共享锁的线程只能读数据,不能修改数据。
独占锁,也叫排他锁。是指该锁一次只能被一个线程所持有。如果线程对数据A加上排他锁后,则其他线程不能再对A加任何类型的锁。获得排他锁的线程即可以读数据也可以写数据。
1.1 乐观锁的实现
1.1.1 版本号控制
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数。当数据被修改时,version值会加1,当线程A要更新数据时,在提交更新时,若在刚读取到的version值与当前数据表的version值相等才更新,否则重试更新操作,直到更新成功。
线程 时间节点 | 线程A | 线程B |
T1 | 读取数据A,其版本号为1 | |
T2 | 读取数据A,其版本号为1 | |
T3 | 提交更新,对比数据表版本号相等,更新成功,版本号变为2 | |
T4 | 提交更新,对比数据库版本号不相等,提交更新失败 | |
T5 | 重新读取数据A,版本号为2 | |
T6 | 提交更新,对比数据表版本号相等,更新成功,版本号变为3 |
表 版本号控制操作说明
1.1.2 CAS算法
CAS(Compare And Swap)比较与交换算法,被广泛应用各大框架中。
实现:维护3个变量,当前内存值V、旧的预期值A、即将更新的值B。通过while循环不断获取当前内存中的值V。如果V=A,就把V值更新为B。整个比较并交换操作是原子操作。伪代码如下:
while(V != A) {}
V = B;
当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。允许其他线程再次尝试,也允许其他线程放弃操作。
问题 | 说明 |
ABA问题 | 线程1 修改变量值A,线程2 也修改这个值,在线程2,将值A改成B最后又改成A。导致线程1能通过CAS检测,能顺利提交修改。(容易产生业务逻辑混乱的问题) |
CPU开销大 | 空循环时间过长,会造成CPU开销很大 |
只能保证一个共享变量的原子操作 | 操作多个共享变量时,循环CAS无法保证操作的原子性,需要用锁来保证原子性 |
表 CAS存在的问题
线程 时间节点 | 小红ATM1 | 小红ATM2 | 小张 |
T1 | 银行卡余额为100,给小李转100 | 给小红帐户转100 | |
T2 | 网络阻塞 | 小红换过另一台机器,继续给小李转100 | |
T3 | 网络阻塞 | 转账成功,小红余额变为0 | |
T4 | 网络阻塞 | 转账成功,小红余额变为100 | |
T5 | 转账成功100,小红余额为0 |
表 银行转账中出现的ABA问题说明
产生的后果是:小红原预期只给小李转100的,但实际却转了200。
1.2 自旋锁
spin lock,当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
图 自旋锁和非自旋锁
1.2.1 自旋锁优缺点
优点:1)自旋锁不会使线程状态发生切换,一直处于用户态,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快;
缺点:1)如果锁被占用的时间很长,那么自旋的线程会浪费很多处理器资源;2)自旋锁非公平的(也可以实现公平的自旋锁),无法满足等待时间最长的线程优先获取锁;
1.2.2 自旋锁Java实现
public class SpinLockDemo {
private static class SpinLock {
private AtomicReference<Thread> owner = new AtomicReference<>();
private volatile int count = 0; //记录锁重入次数
public void lock() {
Thread current = Thread.currentThread();
if (current == owner.get()) {
count++;
return;
}
while (!owner.compareAndSet(null,current));
}
public void unlock() {
Thread current = Thread.currentThread();
if (current == owner.get()) {
if (count > 0) {
count--;
} else {
owner.set(null);
}
}
}
}
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + "开始获取自旋锁");
spinLock.lock();
try {
System.out.println(currentThread.getName() + "获取锁成功");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
spinLock.unlock();
System.out.println(currentThread.getName() + "释放了自旋锁");
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
//运行结果:
//Thread-1开始获取自旋锁
//Thread-0开始获取自旋锁
//Thread-1获取锁成功
//Thread-1释放了自旋锁
//Thread-0获取锁成功
//Thread-0释放了自旋锁
2 ReentrantLock
2.1 与synchronized(lock)并不互斥
public class MutualExclusion {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
System.out.println("synchronized thread1 begin");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread1 end");
}
});
Thread thread2 = new Thread(() -> {
lock.lock();
System.out.println("lock thread2 begin");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread2 end");
lock.unlock();
});
thread1.start();
thread2.start();
}
}
/*
运行结果:
lock thread2 begin
synchronized thread1 begin
thread1 end
thread2 end
*/
2.2 await与signal源码分析
//生产者-消费者模式
public class AwaitAndSignal {
private final static ReentrantLock lock = new ReentrantLock();
private final static Condition condition = lock.newCondition();
private static volatile boolean ready = false;
private static long count = 0;
private static void producer() {
try {
lock.lock();
while (ready) {
condition.await();
}
count++;
System.out.println("生产:" + count);
ready = true;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private static void consumer() {
try {
lock.lock();
while (!ready) {
condition.await();
}
System.out.println("消费:" + count);
ready = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (true) producer();
});
Thread thread2 = new Thread(() -> {
while (true) consumer();
});
thread1.start();
thread2.start();
}
}
/*
运行结果:
生产:306357
消费:306357
生产:306358
消费:306358
生产:306359
消费:306359
*/
图 await()方法源码截图
3 ReentrantReadWriteLock 读写锁
public class Test10 {
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static void read() {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + ",read begin");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ",read end");
lock.readLock().unlock();
}
private static void write() {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + ",write begin");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ",write end");
lock.writeLock().unlock();
}
public static void main(String[] args) throws InterruptedException {
Thread readThread1 = new Thread(() -> {
read();
});
readThread1.setName("readThread1");
Thread readThread2 = new Thread(() -> {
read();
});
readThread2.setName("readThread2");
Thread writeThread1 = new Thread(() -> {
write();
});
writeThread1.setName("writeThread1");
Thread writeThread2 = new Thread(() -> {
write();
});
writeThread2.setName("writeThread2");
writeThread1.start();
// readThread2.start();
TimeUnit.SECONDS.sleep(1);
readThread1.start();
// writeThread2.start();
}
}
/*
运行结果:
writeThread1,write begin
writeThread1,write end
readThread1,read begin
readThread1,read end
Process finished with exit code 0
*/