基础学习-05-java锁

1.公平锁

多个线程按照申请锁的顺序来获取锁。
按照FIFO(先进先出)规则来获取锁。

2.非公平锁

多个线程获取锁的顺序不是按照申请锁的顺序,有可能后申请的线程优先获得锁。
在高并发的情况下,那么就有可能造成优先级反转或饥饿现象。

3.公平锁和非公平锁的获取

并发包中java.util.concurrent.locks.ReentrantLock的创建,可以通过构造函数的布尔值来获取公平锁和非公平锁,默认是非公平锁的。

//java.util.concurrent.locks.ReentrantLock类中
//无参构造,是非公平锁的
public ReentrantLock() {
        sync = new NonfairSync();
}
//通过boolean来获取公平锁或非公平锁
//true为公平锁,false为非公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
4.可重入锁(递归锁)

同一线程外层函数获得锁后,内层递归函数依然能获取该锁。
同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
即:线程可以进入任何一个它已经拥有的锁所同步的代码块。

  • ReentrantLock、Synchronized就是典型的可重入锁。
  • 可重入锁最大作用:避免死锁。
5.自旋锁

尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。
好处:减少线程上下文切换的消耗
缺点:循环获取会消耗CPU

![自旋锁原理][1]

package cn.chen.demo.lockdemo;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 
 * @ClassName:  SpinLockDemo   
 * @Description: 自旋锁的实现   
 * @author: chenlf 
 *  1.原理:循环比较获取直到成功为止,没有类似wait阻塞
 *  2.通过CAS操作完成自旋锁
 *  	
 */
public class SpinLockDemo {
	//原子引用线程
	AtomicReference<Thread> atomicThread = new AtomicReference<>();
	//上锁
	public void myLock() {
		//当前线程对象
		Thread thread = Thread.currentThread();
		System.out.println("当前线程:" + Thread.currentThread().getName()+" 获取锁成功!");
		//比较当前对象,期望值是null,如果atomicThread为null,则将当前线程thread设置进去。
			//此处满足条件,atomicThread.compareAndSet(null, thread)返回值为true
			//取反,因此第一次并没有进while循环体
		while (!atomicThread.compareAndSet(null, thread)) {
			
		}
	}
	//解锁
	public void myUnLock() {
		//当前线程对象
		Thread thread = Thread.currentThread();
		//解锁时变成null
		atomicThread.compareAndSet(thread, null);
		System.out.println("当前线程:" + Thread.currentThread().getName()+" 释放锁成功!");
		
	}
	
	public static void main(String[] args) {
		SpinLockDemo spinLock = new SpinLockDemo();
		//线程A
		new Thread(()->{
			spinLock.myLock();//上锁
			try { TimeUnit.SECONDS.sleep(5);} catch (Exception e) { e.printStackTrace(); }//暂停线程占用锁5秒钟 
			spinLock.myUnLock();//解锁
		},"线程A"	).start();
		//为了确保线程A先获取锁,暂停2秒钟
		try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
		//线程B
		new Thread(()->{
			spinLock.myLock();//上锁
			try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
			spinLock.myUnLock();//解锁
		},"线程B"	).start();
	}
	/**
	 * 运行结果:
	 	当前线程:线程A 获取锁成功!
		当前线程:线程B 获取锁成功!
		当前线程:线程A 释放锁成功!
		当前线程:线程B 释放锁成功!
	   B需要等A用完锁才能用锁
	 */
}

6.读写锁

独占锁:锁一次只能被一个线程所持有。ReentrantLock和Synchronized均是独占锁
共享锁:锁可以被多个线程所持有。
ReentrantReadWritrLock 其读锁是共享锁,写锁则为独占锁。
读锁的共享锁可以保证并发读,效率非常高。读写,写读,写写的过程为互斥的。

package cn.chen.demo.lockdemo;
/**
 * 
 * @ClassName:  ReentrantReadWriteLockDemo   
 * @Description: 读写锁 
 * @author: chenlf 
 *  读:多个线程可以同时读取一个资源类。
 *  写:如果有一个线程想去写共享资源,那么其他线程就无法对该资源进行读或写
 *  因此,读读可以共存,而读写、写写则是互斥的。
 */

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 
 * @ClassName:  MyCache   
 * @Description:模拟缓存 
 * @author: chenlf 
 *
 */
class MyCache{//资源类
	//保证可见性、及时性:一有更新立即通知
	private volatile Map<String,Object> map = new HashMap<>();
	//读写说
	private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
	
	/**
	 * @Title: put   
	 * @Description: 写操作
	 * @author: chenlf  
	 */
	public void put(String key, Object value) {
		rwLock.writeLock().lock();//写锁
		try {
			System.out.println(Thread.currentThread().getName()+" 正在写入:"+key+"="+value);
			//暂停线程占用锁 
			try { TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) { e.printStackTrace(); }
			map.put(key, value);
			System.out.println(Thread.currentThread().getName()+" 写入完成:"+key+"="+value);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			rwLock.writeLock().unlock();//释放锁
		}
	}
	
	/**
	 * @Title: get   
	 * @Description: 读操作
	 * @author: chenlf  
	 */
	public void get(String key) {
		rwLock.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()+" 正在读取:"+key);
			try { TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) { e.printStackTrace(); }
			Object value = map.get(key);
			System.out.println(Thread.currentThread().getName()+" 读取完成:"+value);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			rwLock.readLock().unlock();
		}
	}
	
	/**
	 * 
	 * @Title: clearMap   
	 * @Description: 清除缓存
	 * @author: chenlf  
	 */
	public void clearMap(){
		map.clear();
	}
}

public class ReentrantReadWriteLockDemo {

		public static void main(String[] args) {
			
			MyCache myCache = new MyCache();
			//模拟写操作
			for (int i = 0; i <= 5; i++) {
				final int tempInt = i;
				new Thread(()-> {
					myCache.put(tempInt+"", tempInt+"");
				},String.valueOf(i)).start();
			}
			//模拟读操作
			for (int i = 0; i <= 5; i++) {
				final int tempInt = i;
				new Thread(()-> {
					myCache.get(tempInt+"");
				},String.valueOf(i)).start();
			}
		}
		//写不被打断,读不做限制
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值