公平锁与非公平锁
可重入锁(递归锁)
意思就是当有某一个线程拿到了外层函数的锁之后,它就自动拿到了内层函数的锁。
还有些晦涩难懂?咱们来举个栗子🌰
class phone{
public synchronized void get(){
System.out.println(Thread.currentThread().getId()+"get方法");
set();
}
public synchronized void set(){
System.out.println(Thread.currentThread().getId()+"注意我是set方法");
}
}
public class ReentrantlockDemo {
public static void main(String[] args) throws InterruptedException {
phone phone= new phone();
iphone iphone=new iphone();
new Thread(()->{
phone.get();
}, "t1").start();
new Thread(()->{
phone.get();
}, "t2").start();
TimeUnit.SECONDS.sleep(2);
System.out.println("==========以上是synchronized是递归锁(可重入锁)的验证==========");
}
}
set方法在get方法之内,并且两个方法都加了锁,当线程1,或者线程2拿到了get方法的锁,那么自然也就拿了set方法的锁。看结果`
12get方法
12注意我是set方法
13get方法
13注意我是set方法
==========以上是synchronized是递归锁(可重入锁)的验证==========
接下来验证Reentrantlock也是递归锁。如下
class iphone implements Runnable{
Lock lock =new ReentrantLock();
@Override
public void run() {
get();
}
public void get() {
lock.lock();
try {
System.out.println(Thread.currentThread().getId()+"get方法");
set();
}finally {
lock.unlock();
}
}
public void set(){
lock.lock();
try {
System.out.println(Thread.currentThread().getId()+"注意我是set方法");
}finally {
lock.unlock();
}
}
}
public class ReentrantlockDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("==========以下是Reentrantlock是递归锁(可重入锁)的验证==========");
Thread thread1 =new Thread(iphone);
Thread thread2= new Thread(iphone);
thread1.start();
thread2.start();
}
}
结果:
==========以下是Reentrantlock是递归锁(可重入锁)的验证==========
15get方法
15注意我是set方法
16get方法
16注意我是set方法
自旋锁
首先,自旋是不是很眼熟,当我们在说CAS的时候是不是也谈到了它的自旋,就是因为它的do,while。同样的道理,自旋锁就是线程如果尝试获取锁失败后,不会立即阻塞,而是会采用循环访问的方式获取锁,这样的好处是减少了上下文的切换,缺点是增加了CPU的消耗,真是“成也萧何,败也萧何”。
还是来举个栗子🌰。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLockDemo {
static int num =0;
AtomicReference<Thread> atomicReference =new AtomicReference();
public void lock(){
Thread thread= Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"come on");
while (!atomicReference.compareAndSet(null, thread)){
if (num <= 2) {
num++;
System.out.println(Thread.currentThread().getName()+" 我来看看你这个占着茅坑不拉粑粑的家伙");
}
}
}
public void unlock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName()+"走了");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo =new SpinLockDemo();
new Thread(()->{
spinLockDemo.lock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.unlock();
}, "t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
spinLockDemo.lock();
spinLockDemo.unlock();
}, "t2").start();
}
}
结果:
t1come on
t2come on
t2 我来看看你这个占着茅坑不拉粑粑的家伙
t2 我来看看你这个占着茅坑不拉粑粑的家伙
t2 我来看看你这个占着茅坑不拉粑粑的家伙
t1走了
t2走了
t1先来,看见原子引用是空,然后将原子引用由空变成自己,然后阻塞,当t2线程来的时候先看原子引用里是空吗,不是空,则陷入这个循环,也就是自旋,当然这里不仅仅是自旋三次,我只是为了方便演示三次之后就没有再打印,只有当t1线程醒了的时候,执行了lock.unlock将原子引用由它自己,变为空,才能使的线程二while中的判断为false。线程2才能继续进行。
读写锁
首先来谈谈为什么需要读写锁,读写锁是为何而生的。
它的产生我觉得在于写的特殊性,写不能与读共存,意思就是,当某个线程要开始写时,他必须将他想写的的东西都写完,不然总不能把孩子憋坏吧。还有写更不能与写共存。也就是说,一个线程写的时候别的线程是不能抢占CPU来写,不然的话写的东西岂不是就乱套了。还有就是读和读是可以共存的。我们一起看一眼总不会怎么样吧。
因为这些,如果我们用Reentrantlock,或者synchronized就一下打死了一大片,就像如果我们把只是把它的写方法给锁住之后,还是会有别的线程能调用到他的读方法。如果都锁住,则不能同时读。这就是为何会出现了读写锁。
还是举个栗子🌰:
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class cache{
private volatile HashMap<String,Object> map =new HashMap();
private ReentrantReadWriteLock readWriteLock =new ReentrantReadWriteLock();
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"开始写"+key);
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName()+"写完了");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"开始读");
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+"读结束"+result);
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
public class RAWLockDemo {
public static void main(String[] args) {
cache cache =new cache();
for (int i =0;i<5;i++) {
final int temp = i;
new Thread(() -> {
cache.put(temp+"", temp+"");
}, String.valueOf(i)).start();
}
for (int i =0;i<5;i++) {
final int temp = i;
new Thread(() -> {
cache.get(temp+"");
}, String.valueOf(i)).start();
}
}
结果:作为有读写锁,右为无读写锁