- 从java的集合线程安全开始说起
- 平时经常使用的集合类如ArrayList,LinkedList,HashSet等以及Map都是线程不安全的,如果进行多线程操作会出现线程安全问题,那么java是如何解决这些常用类的线程安全问题呢
- 几个常用的保证集合类线程安全的方法
- 使用synchronized关键字
- 使用Collections提供的工具方法保证线程安全,底层还是使用Synchronized关键字
Collections.synchronizedCollection(list)
Collections.synchronizedList()
Collections.synchronizedSet();
Collections.synchronizedMap(); 使用juc包中的提供的集合类:
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentHashMapcopyOnWrit类在新增元素的时候,会将add方法加锁,防止其他线程调用add方法时产生线程不安全问题。在加锁后,回将底层保存元素的数组copy一份,并利用这个拷贝的副本创建一个新的数组,然后将要追加的元素放到新数组中, 最后保存元素的数组指向包含要追加元素的新数组,然后解锁
使用锁和synchronized来保证线程安全的对比
- 原始构成
- synchronized是java的关键字,是在jvm层面的实现,底层利用moniter实现
- jvm层面在进入被synchronized块包围的代码块是执行 moniterenter命令,跳出时执行moniterexit命令
- lock是在语言层面的实现,它是在jdk5之后新出现的一个java类
- synchronized是java的关键字,是在jvm层面的实现,底层利用moniter实现
- 使用方法
- 使用synchronized关键字不需要用户手动释放锁,即使代码出现异常锁也会被释放
- 使用lock需要手动释放锁,否则会出现死锁情况;配合try--catch-finally代码块使用lock,unlock加锁,释放锁
- 等待是否可中断
- synchronized的不能被中断,一旦进入synchronized代码块,代码要不正常执行完,要么出现异常跳出代码块
- 使用lock可以出现中断
- 是否是公平锁
- synchronized为非公平锁
- lock可以是公平锁也可以是非公平锁,默认是非公平锁
- 锁绑定多个条件condition
- lock可以实现分组唤醒,可以精确唤醒需要唤醒的线程,而synchronized要么唤醒一个线程要么唤醒全部线程,无法精确控制 lock绑定多条件示例代码见 iqijun/javastudy
- 原始构成
- java中锁的分类
- 公平锁和非公平锁
- 公平锁是指按申请锁的顺序来获得锁
- 非公平锁是指随机抢占锁,而不是按照顺序获得
- 非公平锁可能会造成优先级反转问题和饥饿现象(线程长时间获取不到锁),但非公平锁的吞吐量比较大
- ReentrantLock默认为非公平锁,synchronized也是非公平锁
- 可重入锁(递归锁)
- 可重入锁是指当线程获得了外层方法的锁之后,也可以进入到需要相同锁的内部方法。而不需要在进入到内部方法时重新获取锁。
- 比如
/** * @program: course * @description: 重入锁演示 * 重入锁又叫递归锁,当线程在外城方法中获得到锁以后,它边默认可以进入到需要相同锁的内部方法中 * 如示例中methodA和methodB都需要获得lock,当线程在进入到methodA中时获取到lock,那边它便可以顺利进入到methodB中 * 而不需要在进入到methodB的时候重新获取锁 * @author: xingguishuai * @create: 2019-05-25 14:45 */ public class ReentredLockDemo { Lock lock = new ReentrantLock(); public static void main(String[] args){ final ReentredLockDemo reentredLockDemo = new ReentredLockDemo(); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { reentredLockDemo.methodA(); } },"Thread_"+i).start(); } } public void methodA(){ lock.lock(); try { System.out.println(Thread.currentThread().getName()+"进入mthodA,获得到锁"); methodB(); }catch (Exception e){ }finally { lock.unlock(); } } public void methodB(){ lock.lock(); try { System.out.println(Thread.currentThread().getName()+"进入mthodB,获得到锁"); }catch (Exception e){ }finally { lock.unlock(); } } }
- 可重入锁的好处是可以有效避免死锁现象。这是因为减少了锁抢占
ReentrantLock,synchronized都是可重入锁
独享锁、共享锁
- 独享锁是指该锁一次只能被一个线程所持有。共享锁是指该锁可被多个线程所持有。
ReentrantLock,synchronized都是独享锁,ReadWriteLock的readLock是共享锁
- 共享锁和不加锁的区别
加的共享锁之后,就不能再次加独享锁。可以实现多条线程去读,但读的同时不能有写操作
- 独享锁是指该锁一次只能被一个线程所持有。共享锁是指该锁可被多个线程所持有。
自旋锁
自旋锁是指在线程获取锁不成功的时候,不会将线程阻塞,而是通过不断的重试来获取锁.
自旋锁的好处是减少了线程上下文切换的消耗
但由于自旋锁通过循环不停的尝试获取锁,会造成cpu的消耗
- 公平锁和非公平锁
java中锁的实现原理
java中锁是完全利用java语言实现的,它的实现利用了cas原理和AbstractQueuedSynchronizerl类
评论区