看完后,你再也不用怕面试问并发编程啦( 八 )


对于synchronized而言 , 也是一个可重入锁 。可重入锁的一个好处是可一定程度避免死锁 。synchronized void setA() throws Exception{Thread.sleep(1000);setB();}synchronized void setB() throws Exception{Thread.sleep(1000);} 上面的代码就是一个可重入锁的一个特点 。如果不是可重入锁的话,setB可能不会被当前线程执行 , 可能造成死锁 。
公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁 。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁 。
对于Java ReetrantLock而言,通过构造函数指定该锁是否是公平锁 , 默认是非公平锁 。非公平锁的优点在于吞吐量比公平锁大 。
对于synchronized而言,也是一种非公平锁 。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁 , 对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作 。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment , 它即类似于HashMap(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock) 。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作 。
偏向锁/轻量级锁/重量级锁
偏向锁是指一段同步代码一直被一个线程所访问 , 那么该线程会自动获取锁 。降低获取锁的代价 。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁 , 不会阻塞,提高性能 。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋 , 但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁 , 就会进入阻塞,该锁膨胀为重量级锁 。重量级锁会让他申请的线程进入阻塞,性能降低 。
自旋锁
 在Java中 , 自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU 。11、Java中的锁:synchronizedsynchronized是并发编程中接触的最基本的同步关键字 , 是一种重量级锁,也是java内置的同步机制,首先我们知道synchronized提供了互斥性和可见性,那么我们可以通过使用它来保证并发的安全 。
synchronized三种用法:

  • 对象锁
当使用synchronized修饰类普通方法时 , 那么当前加锁的级别就是实例对象,当多个线程并发访问该对象的同步方法、同步代码块时,会进行同步 。
  • 类锁
当使用synchronized修饰类静态方法时,那么当前加锁的级别就是类,当多个线程并发访问该类(所有实例对象)的同步方法以及同步代码块时,会进行同步 。
  • 同步代码块
当使用synchronized修饰代码块时 , 那么当前加锁的级别就是synchronized(X)中配置的x对象实例,当多个线程并发访问该对象的同步方法、同步代码块以及当前的代码块时,会进行同步 。
使用同步代码块时要注意的是不要使用String类型对象 , 因为String常量池的存在,所以很容易导致出问题 。synchronized 同步代码块synchronized 同步代码块解决线程安全问题
什么是线程安全问题: 多线程操作共享数据 , 导致共享数据出现错乱
出现线程安全问题的条件:
1.有多个线程
2.有共享数据
3.多线程操作共享数据
代码:package cn.itcast.thread;/** 学习使用同步代码块解决线程安全问题 */public class Test8 { // 定义票的总数量 private static int ticket = 100; public static void main(String[] args) { Runnable runnable = () ->{ // 循环买票 while (true){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 同步代码块(类锁) synchronized (Test8.class) { if (ticket > 0) { ticket--; System.out.println(Thread.currentThread().getName() + "卖了一张票 , 剩余:" + ticket); } else { // 票没了 break; } } } }; // 创建3个线程 Thread t1 = new Thread(runnable, "窗口1"); Thread t2 = new Thread(runnable, "窗口2"); Thread t3 = new Thread(runnable, "窗口3"); t1.start(); t2.start(); t3.start(); }}


推荐阅读