LK 接着上面的内容继续总结相关知识,新的一年里希望自己能够持续更新自己的技术栈,也祝愿各位浏览我主页的朋友新的一年升职加薪。
公平锁
1.为什么要有公平锁?
是因为出现了不公平--java中称之为饥饿。
> 如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,这种状态被称之为“饥饿”。
总结:是因为自己的cpu运行时间被其他线程占用了。
2.Java中导致饥饿的原因
- 高优先级线程吞噬所有的低优先级线程的CPU时间。(线程优先级)
- 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。(Java的同步代码区对哪个线程允许进入的次序没有任何保障)
- 线程在等待一个本身(在其上调用wait())也处于永久等待完成的对象,因为其他线程总是被持续地获得唤醒。(多个线程调用wait()方法,使用nofity方法唤醒了其中不确定的一个,总有一个没被唤醒)
公平锁原理
public class QueueObject {
private boolean flag=false;
public synchronized void doWait() throws InterruptedException {
//自旋,
while (!flag) {
this.wait();
}
flag=false;
}
public synchronized void doNotify() {
flag=true;
this.notify();
}
@Override
public boolean equals(Object obj) {
return this==obj;
}
}
public class FairLock {
// private boolean flag=false;
private Thread currenThread = null;
List<QueueObject> queueList = new ArrayList<QueueObject>();
private void MyLock() {
QueueObject objectQueue = new QueueObject();
currenThread = Thread.currentThread();
synchronized (this) {
queueList.add(objectQueue);
}
try {
objectQueue.doWait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
synchronized (this) {
queueList.remove(objectQueue);
}
}
}
private void MyUnlock() {
if (currenThread != Thread.currentThread())
throw new IllegalMonitorStateException("Calling thread has not locked this lock");
else
currenThread = null;
if (queueList.size() > 0) {
queueList.get(0).doNotify();
}
}
}
主要是内部维护了一个队列,第一个线程在队列中添加完一个元素后,其他线程添加完元素会自旋阻塞,等待队列头部元素唤醒。下一个元素进入,排队机制可见应用还是相当广泛的。
读写锁
1.什么是读写锁?
读写锁是一种特殊的自旋锁,它把对共享资源对访问者划分成了读者和写者,读者只对共享资源进行访问 ,写者则是对共享资源进行写操作 。
读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升
2.使用场景?
读操作多于写操作时
3.简单实例
public class MyReadWriteLock {
private ReadWriteLock rwl = new ReentrantReadWriteLock();
private Map<String, Object> map = new HashMap<>();
public void setMap(String string, Object object) {
rwl.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
map.put(string, object);
} finally {
rwl.writeLock().unlock();
System.out.println(Thread.currentThread().getName() + " 写操作执行完毕");
}
}
public Object getMap(String key) {
rwl.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map.get(key);
} finally {
rwl.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " 读操作执行完毕");
}
}
}
public static void main(String args[]) {
MyReadWriteLock lock=new MyReadWriteLock();
lock.setMap("aa", "xxxxx");
ExecutorService service = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(
Thread.currentThread().getName() + "..." +lock.getMap("aa"));
}
});
}
}
4.读写锁释放和获取
总结: 每次释放均减少写状态,当写状态为0时表示写锁已被释放,从而等待的读写线程能够继续访问读写锁,同时前次写线程的修改对后续读写线程可见。
读锁的每次释放均(线程安全的,可能有多个读线程同时释放读锁)减少读状态
之后在分析AQS时再探讨
5.锁降级
过程:获取写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程
目的:自我感悟是减少写锁的持有,提高性能。
public class LockDown {
private Map<String, Object> map = new HashMap<>();
private ReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock r = rwl.readLock();
private Lock w = rwl.writeLock();
private volatile boolean isUpdate = false;
public void readWrite() {
r.lock(); // 为了保证isUpdate能够拿到最新的值
if (!isUpdate) {
r.unlock();
w.lock();
try {
if (!isUpdate) {
// 准备数据的
map.put("asdsaf", "12314");
}
r.lock();// 获取读锁,保证数据的可见性。
// 直接释放写锁,会导致其他线程对数据进行跟新但是当前线程无法感知
// 获取读锁后其他线程会被阻塞,直到当前线程使用数据后,其它线程才可以获取。
} finally {
w.unlock();
}
}
try {
// 使用数据
Object obj = map.get("asdsaf");
System.out.println(obj);
} finally {
r.unlock();
}
}
public Object get(String key) {
r.lock();
System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
try {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return map.get(key);
} finally {
r.unlock();
System.out.println(Thread.currentThread().getName() + " 读操执行完毕..");
}
}
public void put(String key, Object value) {
w.lock();
System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
try {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
} finally {
w.unlock();
System.out.println(Thread.currentThread().getName() + " 写操作执行完毕..");
}
}
public static void main(String[] args) {
LockDown lockDown = new LockDown();
new Thread(new Runnable() {
@Override
public void run() {
lockDown.readWrite();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lockDown.readWrite();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lockDown.readWrite();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lockDown.readWrite();
}
}).start();
}
}