各种锁的理解
1、公平锁、非公平锁
公平锁:非常公平,任何线程获得公平锁,那么就会执行锁中业务直到结束,过程中任何进程都不得干预打扰。不能插队。
非公平锁:在获得非公平锁之后,执行代码过程中,其他线程可以插入执行,该线程暂停执行,等待其他线程执行完毕,才可继续执行。可插队。(默认非公平锁)
2、共享锁、独占锁
共享锁:就是读取锁里的读锁,进行读操作,任何经常都可获得该锁。
独占锁:就是读取锁里的写锁,只允许一个线程获得该锁。
3、可重入锁
可重入锁(递归锁):拿到了外面的锁,内部的锁也都统统拿到了。
lock锁与synchronized锁都是可重入锁,但是有一定的区别。
lock锁必须配对,有上锁对应必须就有解锁,不然会死锁。
4、自旋锁
上图,CAS的源码分析就是一个自旋锁。自旋锁一定是使用CAS做为底层。因为CAS是C++写的原语操作。
自定义简单的锁
public class MyLock {
private AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock(){
// 获取当前执行的线程
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+ " => 正在获取自旋锁");
// CAS操作,自旋锁
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void unLock(){
Thread thread = Thread.currentThread();
// 解锁,将thread赋值为null,上面的自旋锁就可以解除
atomicReference.compareAndSet(thread,null);
}
}
class MyLockTest{
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(()->{
myLock.lock();
try{
System.out.println("A线程正在执行...");
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("A线程执行完毕!");
myLock.unLock();
}
},"A").start();
new Thread(()->{
myLock.lock();
try{
System.out.println("B线程正在执行...");
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("B线程执行完毕!");
myLock.unLock();
}
},"B").start();
}
}
输出:
A => 正在获取自旋锁
A线程正在执行...(等5秒)
B => 正在获取自旋锁
A线程执行完毕!
B线程正在执行...
B线程执行完毕!
5、死锁
就是两个线程都持有各自的锁,但是又要获取对方的锁,此时对方的锁都没有释放,那么就会进入死锁状态。
public class DieLock implements Runnable{
private String lock1;
private String lock2;
public DieLock(String lockA, String lockB) {
this.lock1 = lockA;
this.lock2 = lockB;
}
@Override
public void run() {
synchronized (lock1){
System.out.println(Thread.currentThread().getName() + "要获取" + lock2);
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2){
System.out.println(Thread.currentThread().getName() + "要获取" + lock1);
}
}
}
}
class DieTest{
public static void main(String[] args) {
String lockA = "LockA";
String lockB = "LockB";
// 注意锁的对象
new Thread(new DieLock(lockA,lockB),"1 ").start();
new Thread(new DieLock(lockB,lockA),"2 ").start();
}
}
输出:
1 要获取LockB
2 要获取LockA
(程序进入无限死锁状态...)
死锁排查 jps
除了看日志,还可以看堆栈信息。
- 可在Terminal下输入
jps -l
(java process)定位正在进行的进程号:
- 查询到编号为13860的进程,是DieTest类的。可通过
jstack 13860
(java stack)查看具体信息:
可查到详细信息,两个进程互相等待对方的锁。
关于进程、线程、并发、并行的问题
-
Java中默认有几个线程?
默认2个线程,一个main线程,一个GC线程
-
Java真的能开启线程吗?
不能。
通过源码
private native void start0();
分析,java只能通过本地方法去调用开启线程。由底层c语言区操作的。 -
并发与并行的区别?
并发是多个线程同一时间段共同执行一个资源;
并行是多个线程同一时刻同行。