java并发锁
乐观锁与悲观锁
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。
乐观锁的特点:
ABA 问题:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?
很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 “ABA” 问题。
循环时间长开销大:在特定场景下会有效率问题。
自旋 CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给 CPU 带来非常大的执行开销。
atomic实现乐观锁
public class DemoTest extends Thread{ //Atomic 操作,引入AtomicInteger。这是实现乐观锁的关键所在。 private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { count.incrementAndGet(); } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); } }
synchronized实现悲观锁
public class DemoTest01 extends Thread{ private static int count = 0; //定义count = 0 public static void main(String[] args) { for (int i = 0; i < 2; i++) { //通过for循环创建两个线程 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { synchronized (DemoTest.class){ count++; } } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); } }
独占锁与共享锁
独占锁:每次只能有一个线程持有锁,比如 ReentrantLock 就是以独占方式实现的互斥锁;
共享锁:允许多个线程同时获取锁,并发访问共享资源,比如 ReentrantReadWriteLock。
公平锁与非公平锁
公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。
非公平锁:非公平锁则在运行时闯入,不遵循先到先执行的规则。
//公平锁 ReentrantLock pairLock = new ReentrantLock(true); //非公平锁 ReentrantLock pairLock1 = new ReentrantLock(false); //如果构造函数不传递参数,则默认是非公平锁。 ReentrantLock pairLock2 = new ReentrantLock();
自旋锁
自旋锁:自旋锁则是当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃 CPU 使用权的情况下,多次尝试获取(默认次数是 10,可以使用-XX:PreBlockSpinsh 参数设置该值)。
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者 class),不会因为之前已经获取过还没释放而阻塞。
Java 中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
ReentrantLock 重入验证
/** * @author wsy */ public class DemoTest03 { //ReentrantLock可重入验证 public static void main(String[] args) { new Thread(new ReentrantLockTest()).start(); } } class ReentrantLockTest implements Runnable { Lock lock = new ReentrantLock(); public void helloA() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " helloA()"); helloB(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void helloB() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " helloB()"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } @Override public void run() { helloA(); } }
synchronized 可重入锁验证
public class DemoTest04 { //synchronized可重入锁验证 public static void main(String[] args) { new Thread(new SycDemo()).start(); } } class SycDemo implements Runnable{ public synchronized void helloA(){ System.out.println(Thread.currentThread().getName() + " helloA()"); helloB(); } public synchronized void helloB(){ System.out.println(Thread.currentThread().getName() + " helloB()"); } @Override public void run() { helloA(); } }

浙公网安备 33010602011771号