Java 锁

公平锁/非公平锁:

公平锁是指多个线程按照申请锁的顺序来获取锁,非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程优先获取锁。
非公平锁的优点:在于可以减少唤起线程的开销,吞吐量比公平锁大,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程;公平锁的优点是等待锁
非公平锁的缺点:可能会造成优先级反转或者饥饿现象,或者等很久才会获得锁
ReentrantLock通过构造函数指定该锁是否是公平锁,默认是非公平锁;synchronized是非公平锁
公平锁的优点:等待锁的线程不会饿死
公平锁的缺点:整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销大

 

可重入锁:

可重入锁有名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法时会自动获取锁,ReentrantLock和synchronized都是可重入锁(sleep方法不会释放对象锁,wait方法会释放)

import java.util.concurrent.TimeUnit;
/**
 * 验证synchronized的可重入性
 * @author 20644
 *
 */
public class ReentrantTest implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			
			System.out.println(Thread.currentThread().getName()+"running");
			method1();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private synchronized void method1() throws InterruptedException {
			System.out.println(Thread.currentThread().getName()+"method1 start");
			TimeUnit.SECONDS.sleep(1);
			method2();
	
	}
	
	private synchronized void method2() throws InterruptedException{
			System.out.println(Thread.currentThread().getName()+"method2 start");
			TimeUnit.SECONDS.sleep(1);
	}
}

public class Entrance2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ReentrantTest test = new ReentrantTest();
		Thread thread4 = new Thread(test,"测试窗口1");
		Thread thread5 = new Thread(test,"测试窗口2");
		thread4.start();
		thread5.start();
	}

}
运行结果:
测试窗口1running
测试窗口2running
测试窗口1method1 start
测试窗口1method2 start
测试窗口2method1 start
测试窗口2method2 start

 

独享锁/共享锁:

独享锁是指该锁一次只能被一个线程所持有,共享锁是指该锁可被多个线程所持有;
对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,写锁是独享锁;
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享

 

乐观锁/悲观锁:

乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候回判断一下在此期间别人有没有去更新这个数据,乐观锁适用于多读的应用类型,乐观锁在Java中是通过使用无锁编程来实现的,最常用的是CAS算法,Java原子类中的递增操作就是通过CAS自旋实现的。CAS(比较与交换),是一种无锁算法,在不使用锁的情况下实现多线程之间的变量同步,CAS算法有3个操作数:需要读写的内存值V,进行比较的值A,要写入的新值B,仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。
悲观锁:每次去拿数据的时候都会认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞知道它拿到锁,synchronized就是悲观锁,悲观锁适合写操作多的场景,乐观锁适合读操作多的场景。

 

自旋锁:

自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗
JVM对于自旋周期的选择,jdk1.5这个限度是一定的写死的,在1.6引入了适应性自旋锁,适应性自旋锁意味着自旋的时间不在是固定的了,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定,基本认为一个线程上下文切换的时间是最佳的一个时间,同时JVM还针对当前CPU的负荷情况做了较多的优化:如果平均负载小于CPUs则一直自旋;如果平均负载小于CPUs则一直自旋;如果有超过(CPUs/2)个线程正在自旋,则后来线程直接阻塞;如果正在自旋的线程发现Owner发生了变化则延迟自旋时间(自旋计数)或进入阻塞;如果CPU处于节电模式则停止自旋;自旋时间的最坏情况是CPU的存储延迟(CPU A存储了一个数据,到CPU B得知这个数据直接的时间差);自旋时会适当放弃线程优先级之间的差异
自旋锁的开启:
JDK1.6中-XX:+UseSpinning开启; 
-XX:PreBlockSpin=10 为自旋次数; 
JDK1.7后,去掉此参数,由jvm控制;

 

4种Java线程锁(线程同步)

1.synchronized
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的,原因在于编译程序通常会尽可能的优化synchronized,另外可读性非常好。
2.ReentrantLock
在资源竞争不激烈的情况下,性能比synchronized差一点,但是在同步非常激烈地时候,synchronized性能一下子下降好几十倍,而ReentrantLock还能维持常态,所以在高并发量情况下使用ReentrantLock。
3.Semaphore
4.AtomicInteger

与ReentrantLock类似,适合高并发,并且性能还更好,但是有一个缺点,只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效

参考资料:https://blog.youkuaiyun.com/u013030086/article/details/85001354#1_40
                  https://blog.youkuaiyun.com/zqz_zqz/article/details/70233767

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值