java锁总结

声明,本文并不是一篇关于java锁的系统文章,纯属个人的学习记录(可能会存在一些错误的理解)。。。想要学习锁的伙伴请自行阅读并发编程相关的书籍,或者直接搜索“concurrent”关键字找到相关内容。。。其次,本文的写作背景源于面试之前的死记硬背

对象头

每个java对象反编译后都可以看到对象头信息,对象头又称MarkDown。对象头会包含GC信息和这个对象的锁等级信息(无锁,偏向锁、轻量级锁、重量级锁)、以及其他的一些信息

线程间的同步

线程间的同步,又称线程间的通讯。怎么理解线程之间的通讯?我认为是线程A和线程B都能互相感知对方的线程状态(处于挂起、预备状态,还是正在占有时间片or其他的状态)、并能够通知或唤醒对方去做某些事情

线程同步的方式

  1. 通过共享内存同步,体现为用static关键字去修饰某个变量。怎么理解这个东西呢?就是多个线程都能去同一个地方拿到该值了,可不就是同步了嘛

  2. 通过synchronized关键字同步

  3. 通过volitle关键字同步,volitle有两大特性:可见性和有序性。1.怎么理解可见性?就是假设有线程A、B,A在做某项操作,B所能看到的A的状态永远是最新的,是有感知的,不会读到A修改之前的那些“旧缓存”而引发脏读。2.如何理解有序性,即加上该关键字,jvm会对被修饰的部分禁止指令重排,以保证代码的执行都是有序的

  4. 通过notify之类的操作去唤醒或操作线程

  5. 好像还有其他方式,但是现在暂时想不起来了

synchronized关键字

这玩意就是锁,这个sync锁可以锁很多东西,锁住类啊、锁住实例对象啊巴拉巴拉的。

怎么理解锁的定义呢?

第一:被这个synchronized关键字修饰的代码块,在被线程A获取到锁的期间,是被线程A独占的,线程B此时将无法执行这段代码,因此也将无法操作这段代码块中出现的公共资源
第二:这段代码块里面出现的代码,都变成了原子性操作了。??(这句存疑)

另外,synchronized关键字好像在不同的JDK版本下有不一定的表现,JDK1.8及之后的版本下(貌似是这个版本,记得不太清了)

volitle关键字

被volitle关键字修饰变量有两大特性:顺序性和可见性

信号量Semaphore

略,这个我没怎么了解过

锁升级

一个对象的初始状态是无锁的,如果此时有个线程A来获取它,这个锁将会升级为偏向锁。偏向锁意味着,该对象下一次如果仍是被线程A获取到的话,获取结束后,该对象会变回无锁状态。但如果在偏向锁状态下,此时又出现了线程B想要获取锁,那么此时锁将会升级为轻量级锁。
若此时再有个线程C来获取锁,那么没有获取到锁的线程就会一直自旋去获取锁,自旋达到一定次数的时候,锁将会升级成重量级锁,这意味着:除了已经拿到锁的线程外,其他线程都将被阻塞挂起

锁,在设计模式里面的一些应用

1.单例模式,有多种写法。普通的写法是线程不安全的。可以使用synchronized关键字加在生成实例的那个方法上、或者直接写成双检锁的形式来保证单例模式的线程安全

2.双检锁与直接sync锁住整个实例生成方法的对比是:其实实例只要在第一次初始化的时候没有线程来并发生成就好了,剩下的第二次第三次第n次去尝试获取实例,其实都是没有必要锁上生成实例的方法的、这样锁会使获取实例变得效率低下。而双检锁这种写法就避免了这个问题

各种单例模式讲解视频-寒食君

3.两个demo
example_1:用synchronized直接锁住实例生成的方法

public class TestSingleton {

    /**
     * 单例模式
     * 0.全局只有一个实例,要用static关键字
     * 1.自己的实例由自己产生,不被别new
     * 2.对外提供一个获取实例的方法
     */

    private static TestSingleton singleton;

    // 实例获取
    public static synchronized TestSingleton getInstance() {
        if(singleton == null) {
            singleton = new TestSingleton();
        }
        return singleton;
    }

}

example_2:synchronized实现双检锁

public class Singleton{  
  private static Singleton single;    //声明静态的单例对象的变量  
  private Singleton(){}    //私有构造方法   
    
  public static Singleton getSingle(){    //外部通过此方法可以获取对象    
    if(single == null){     
        synchronized (Singleton.class) {   //保证了同一时间只能只能有一个对象访问此同步块        
            if(single == null){      
                single = new Singleton();          
        }     
      }  
    }    
    return single;   //返回创建好的对象   
  }  
}  

锁,在集合里面的一些体现

通常我们聊一个集合,都会从性能、是否线程安全、扩容机制、底层结构去考量它。
锁的应用主要出现在并发包,或者说是concurrent包。
比较常见的比较容易被面试官问到的是ConcurrentHashMap和CopyOnWriteArrayList。这俩货值得好好看看然后出去吹牛逼

CAS和自旋

不知道这两个是不是同一个东西,我总把它们当做同一个意思去理解

总结与规律

1.感觉这些锁或者用到锁的集合在不同的jdk版本中的实现都有点差别,但是肯定都是往更好的方向去演变了。
具体是啥方向呢?
一个是性能方面:有的是通过增加自旋操作来避免线程阻塞所产生的耗时等待;
另一个是,锁的粒度都在变得更小,因此更支持并发了。比如HashMap里的锁,jdk1.7用的是分段锁,一锁会锁住一个段,但jdk1.8时就直接改为数组加链表结构了,一上锁只会锁住某个hashcode的那个链表或红黑树。如此一来锁的粒度就变小了,对并发的支持就更好了。

2.以上虽然总结的是java线程之间的通讯,但是一通百通,其实操作系统间的进程或线程通讯也是类似的,因为有很多相似的说法。。怎么说呢,不要单独地现在java的理解里,会有局限的

3.最后来个小问题,哈哈哈,你知道concurrent这个单词是啥意思嘛?这里说来特好笑,我实习生那会背ConcurrentHashMap时根本就没想过concurrent翻译过来是啥意思,也不知道这个就是“并发”的意思,就傻愣愣地记,没有理解。。。我举这个栗子就是为了emmm学习还是要吹毛求疵一点吧

4.关于并发的术语好多好多好多,反正先理解术语再去“死记硬背”吧

参考材料

线程通讯的四种方式

synchronized原理讲解+锁升级

寒食君讲解单例模式的视频

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值