背景
最近复习一下多线程,上面说到synchronized是将被阻塞的现场放到一个虚拟队列(Lock-Free无锁机制)中,而Lock是通过AQS队列存放阻塞的线程。那synchronized里的线程默认是怎么竞争锁的,Lock又是如何?
之前记录的结论:
思路
开启一个死循环,然后让多个线程竞争通一把锁,被阻塞的线程孜然就会被放到对应的阻塞队列里,执行时打印线程的名字,然后根据打印的结果判断顺序
先验证synchronized的情况,创建一个runnable实现类,然后共享成员变量map
public class MyThread implements Runnable{
Map<String,String> map;
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (map){
System.out.println(Thread.currentThread().getName()+"拿到了锁");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
}
}
}
}
在主方法中执行
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
MyThread myThread = new MyThread();
myThread.setMap(map);
Thread thread = new Thread(myThread);
System.out.println(thread.getName()+"开始启动了");
thread.start();
}
System.out.println("主线程结束了");
}
结果:线程竞争结果无顺序,可以认为当一个线程释放锁资源时,队列中的其他线程都在竞争?
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-5开始启动了
Thread-6开始启动了
Thread-7开始启动了
Thread-8开始启动了
Thread-9开始启动了
主线程结束了
Thread-0拿到了锁
Thread-0结束了
Thread-9拿到了锁
Thread-9结束了
Thread-1拿到了锁
Thread-1结束了
Thread-4拿到了锁
Thread-4结束了
Thread-6拿到了锁
Thread-6结束了
Thread-2拿到了锁
Thread-2结束了
Thread-8拿到了锁
Thread-8结束了
Thread-7拿到了锁
Thread-7结束了
Thread-5拿到了锁
Thread-5结束了
Thread-3拿到了锁
Thread-3结束了
Thread-5拿到了锁
Thread-5结束了
Thread-7拿到了锁
Thread-7结束了
Thread-8拿到了锁
Thread-8结束了
Thread-2拿到了锁
验证Lock锁的情况,通过共享一个ReentrantLock对象。
public class MyThread2 implements Runnable{
ReentrantLock reentrantLock ;
public ReentrantLock getReentrantLock() {
return reentrantLock;
}
public void setReentrantLock(ReentrantLock reentrantLock) {
this.reentrantLock = reentrantLock;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"拿到了锁");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
reentrantLock.unlock();
}
}
}
主线程
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock(false);
for (int i = 0; i < 5; i++) {
MyThread2 myThread2 = new MyThread2();
myThread2.setReentrantLock(reentrantLock);
Thread thread = new Thread(myThread2);
System.out.println(thread.getName()+"开始启动了");
thread.start();
}
}
结果可见,线程执行是有顺序的,确实是按照顺序不停更换头结点,循环,有序拿到锁的。
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-2拿到了锁
Thread-2结束了
Thread-3拿到了锁
Thread-3结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-2拿到了锁
Thread-2结束了
Thread-3拿到了锁
Thread-3结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-2拿到了锁
Thread-2结束了
Thread-3拿到了锁
Thread-3结束了
Thread-4拿到了锁
这里有个疑问就是公平锁的问题,公平锁反应的应该是新来的线程是否应该排队吧,已经在CLH队列里的线程就是按照顺序执行,跟是否公平无关吧?
这里证明一下,先让5个线程都打印完,得以判断出最初的队列顺序,然后再突然让5个新的线程去竞争,看看这5个线程是否会排到后面。
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock(false);
for (int i = 0; i < 5; i++) {
MyThread2 myThread2 = new MyThread2();
myThread2.setReentrantLock(reentrantLock);
Thread thread = new Thread(myThread2);
System.out.println(thread.getName()+"开始启动了");
thread.start();
}
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始加入额外线程");
for (int i = 0; i < 10; i++) {
MyThread2 myThread2 = new MyThread2();
myThread2.setReentrantLock(reentrantLock);
Thread thread = new Thread(myThread2);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.start();
}
System.out.println("主线程结束了");
}
设置的Lock锁为false,为不公平锁
最初顺序为3,2,4,1,0,最后结果成了3,10,5,11,2,12,6,13,4,14,7,1,8等等
根据结果可以发现确实有插队情况出现了,确实符合理论。
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-3拿到了锁
Thread-3结束了
Thread-2拿到了锁
Thread-2结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-3拿到了锁
开始加入额外线程
Thread-3结束了
Thread-2拿到了锁
Thread-2结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-3拿到了锁
Thread-3结束了
Thread-5拿到了锁
Thread-5结束了
Thread-2拿到了锁
Thread-2结束了
Thread-6拿到了锁
Thread-6结束了
Thread-4拿到了锁
Thread-4结束了
Thread-7拿到了锁
主线程结束了
Thread-7结束了
Thread-1拿到了锁
Thread-1结束了
Thread-8拿到了锁
Thread-8结束了
Thread-0拿到了锁
Thread-0结束了
Thread-9拿到了锁
Thread-9结束了
Thread-3拿到了锁
Thread-3结束了
Thread-10拿到了锁
Thread-10结束了
Thread-5拿到了锁
Thread-5结束了
Thread-11拿到了锁
Thread-11结束了
Thread-2拿到了锁
Thread-2结束了
Thread-12拿到了锁
Thread-12结束了
Thread-6拿到了锁
Thread-6结束了
Thread-13拿到了锁
Thread-13结束了
Thread-4拿到了锁
Thread-4结束了
Thread-14拿到了锁
Thread-14结束了
Thread-7拿到了锁
Thread-7结束了
Thread-1拿到了锁
Thread-1结束了
Thread-8拿到了锁
现在改成ture,也就是公平锁,看看结果如何
结果显示不符合预期效果,想了一下,因为CLH他是一个带头结点的双向链表,也就是说他的链表是一直在变化的,所以顺序肯定不会是固定的,头结点释放以后,多个线程同时竞争尾部,然后再依次往前排,顺序势必会打乱。
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-3拿到了锁
Thread-3结束了
Thread-2拿到了锁
Thread-2结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-3拿到了锁
开始加入额外线程
Thread-3结束了
Thread-2拿到了锁
Thread-2结束了
Thread-4拿到了锁
Thread-4结束了
Thread-1拿到了锁
Thread-1结束了
Thread-0拿到了锁
Thread-0结束了
Thread-3拿到了锁
Thread-3结束了
Thread-5拿到了锁
Thread-5结束了
Thread-2拿到了锁
Thread-2结束了
Thread-6拿到了锁
Thread-6结束了
Thread-4拿到了锁
Thread-4结束了
Thread-7拿到了锁
主线程结束了
Thread-7结束了
Thread-1拿到了锁
Thread-1结束了
Thread-8拿到了锁
Thread-8结束了
Thread-0拿到了锁
Thread-0结束了
Thread-9拿到了锁
Thread-9结束了
Thread-3拿到了锁
Thread-3结束了
Thread-10拿到了锁
Thread-10结束了
Thread-5拿到了锁
Thread-5结束了
Thread-11拿到了锁
Thread-11结束了
Thread-2拿到了锁
Thread-2结束了
Thread-12拿到了锁
Thread-12结束了
Thread-6拿到了锁
Thread-6结束了
Thread-13拿到了锁
Thread-13结束了
Thread-4拿到了锁
Thread-4结束了
Thread-14拿到了锁
Thread-14结束了
Thread-7拿到了锁
Thread-7结束了
Thread-1拿到了锁
Thread-1结束了
Thread-8拿到了锁
进一步改造一下ReentrantLock ,打印出他的队列内的元素。
public class ReentrantLockMine extends ReentrantLock {
public ReentrantLockMine(boolean fair) {
super(fair);
}
@Override
protected Collection<Thread> getQueuedThreads() { //获取同步队列中的线程
List<Thread> arrayList = new ArrayList<Thread>(super.getQueuedThreads());
Collections.reverse(arrayList);
return arrayList;
}
}
这边也是改一下内部的成员变量
public class MyThread2 implements Runnable{
ReentrantLockMine reentrantLock ;
public ReentrantLock getReentrantLock() {
return reentrantLock;
}
public void setReentrantLock(ReentrantLockMine reentrantLock) {
this.reentrantLock = reentrantLock;
}
@Override
public void run() {
while (true){
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"拿到了锁,当前队列:"+((ReentrantLockMine)reentrantLock).getQueuedThreads());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
reentrantLock.unlock();
}
}
}
测试方法
public static void main(String[] args) {
ReentrantLockMine reentrantLock = new ReentrantLockMine(false);
for (int i = 0; i < 5; i++) {
MyThread2 myThread2 = new MyThread2();
myThread2.setReentrantLock(reentrantLock);
Thread thread = new Thread(myThread2);
System.out.println(thread.getName()+"开始启动了");
thread.start();
}
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始加入额外线程");
for (int i = 0; i < 10; i++) {
MyThread2 myThread2 = new MyThread2();
myThread2.setReentrantLock(reentrantLock);
Thread thread = new Thread(myThread2);
thread.start();
}
System.out.println("主线程结束了");
}
非公平结果:
可以发现,之后加入的线程全部按照顺序排在了后面,0号线程拿到线程后又插队获取到锁,继续执行,其他线程拿不到锁。
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-0拿到了锁,当前队列:[]
Thread-0结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main]]
Thread-0结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main]]
开始加入额外线程
主线程结束了
Thread-0结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-12,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-9,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main]]
Thread-0结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-12,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-9,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main]]
Thread-0结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-12,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-9,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main]]
公平锁结果:
可以发现全部按照队列顺序去执行了,不存在某个线程一直获取资源,符合预期效果。
Thread-0开始启动了
Thread-1开始启动了
Thread-2开始启动了
Thread-3开始启动了
Thread-4开始启动了
Thread-0拿到了锁,当前队列:[]
Thread-0结束了
Thread-1拿到了锁,当前队列:[Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main]]
Thread-1结束了
Thread-4拿到了锁,当前队列:[Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main]]
开始加入额外线程
主线程结束了
Thread-4结束了
Thread-2拿到了锁,当前队列:[Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main]]
Thread-2结束了
Thread-3拿到了锁,当前队列:[Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main]]
Thread-3结束了
Thread-0拿到了锁,当前队列:[Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main]]
Thread-0结束了
Thread-1拿到了锁,当前队列:[Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main]]
Thread-1结束了
Thread-5拿到了锁,当前队列:[Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main]]
Thread-5结束了
Thread-7拿到了锁,当前队列:[Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main]]
Thread-7结束了
Thread-6拿到了锁,当前队列:[Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main]]
Thread-6结束了
Thread-8拿到了锁,当前队列:[Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main]]
Thread-8结束了
Thread-9拿到了锁,当前队列:[Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main]]
Thread-9结束了
Thread-10拿到了锁,当前队列:[Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main]]
Thread-10结束了
Thread-11拿到了锁,当前队列:[Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main]]
Thread-11结束了
Thread-12拿到了锁,当前队列:[Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main]]
Thread-12结束了
Thread-13拿到了锁,当前队列:[Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main]]
Thread-13结束了
Thread-14拿到了锁,当前队列:[Thread[Thread-4,5,main], Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main]]
Thread-14结束了
Thread-4拿到了锁,当前队列:[Thread[Thread-2,5,main], Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main]]
Thread-4结束了
Thread-2拿到了锁,当前队列:[Thread[Thread-3,5,main], Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main]]
Thread-2结束了
Thread-3拿到了锁,当前队列:[Thread[Thread-0,5,main], Thread[Thread-1,5,main], Thread[Thread-5,5,main], Thread[Thread-7,5,main], Thread[Thread-6,5,main], Thread[Thread-8,5,main], Thread[Thread-9,5,main], Thread[Thread-10,5,main], Thread[Thread-11,5,main], Thread[Thread-12,5,main], Thread[Thread-13,5,main], Thread[Thread-14,5,main], Thread[Thread-4,5,main], Thread[Thread-2,5,main]]
结论
synchronized的队列似乎并不有序,根据Lock-Free的解释,它属于一个虚拟的队列,队列里的各个数据都在竞争锁。
Lock严格按照队列顺序执行,若非公平锁状态则可能被非队列中的现场插队执行,公平锁状态下按照队列顺序,只有头结点才回去尝试获取锁,和synchronized都在竞争锁情况相比可能更节省资源?但是同一时刻多线程入队的顺序是不确定的(雾)。