Thread 同步锁之 synchronized

小伙伴们没想到我们又见面了吧^ ^
不知道大伙面试的时候是不是经常碰到一些问线程同步的问题的面试官,然后你就会觉得很慌有没有
今天我们就来聊聊线程锁中的  synchronized 函数吧
首先synchronized函数可传递任何参
比如类/变量
下面就是传入类的效果

可以看到一旦有线程进入另一个线程只能等待先进入的线程处理完成事件后才会执行下一个线程
 

现在再来看看传入参数的情况

可以看到运行的时两个线程是交叉的,也就是说线程不会等待
但是各自拿到的值都是正确的,都有进行逻辑运行然后得到值
看到这里我相信大家都知道怎么去使用  synchronized   函数了,如果是多个线程执行要保证数据的正确性传入当前可以变参是最合适不过的了
当然传入类对象也是可以保持同步的,但是使用 类对象 就会导致一个性能问题 进行排队等候

那么有性能问题我们要不要解决呢  ---->  来自产品经理的灵魂审问

而此时此刻开发者的一般思维时 -------->  这玩意这么费事,目前又不影响我程序运行 ,而且我对这个锁又不是很熟悉,写个锤子给他写,于是找个理由推掉了或者没有说出这么个加锁法    隐藏在后面迭代可能会导致的性能问题
看到这个功能已经完成了于是乎就跑去划水了,小伙子这是不行的啊 ,你就没想过后面出现多个线程操作这个方法的时候吗 -------->  可能你会自信的说,不会的,这小公司怎么会出现多个线程操作同一个数据呢
好吧,你赢了,   但是你还是要学习多线程操作同一个方法时出现的性能
于是乎就有了下面的优化方法

如果说  synchronized 锁一个对象需要等待,那我多个线程操作的时候锁多个对象不就行,对!  没错 就是锁多个对象,看到这里聪明的人已经看出 上面 synchronized  可变参数 number 不就是锁多个对象吗,漂亮,就是的
那如果说多个类对象进行操作呢,比如说 有两个TestData  testData1,TestData testData2.  synchronized  锁住这两个对象呢是不是也可以,答案当然是可以的

但是会出现一个问题如果 线程穿插  synchronized  不同的对象时及其容易导致死锁,我们来看一下下面的这个情况

你们能从这里看出一些什么端倪了吗,     我大概猜到了你会说啥  --------->   看你大爷谁知道你写的是啥啊,我要是懂锁还来看你的文章?
哈哈,不买管子了,注意看这里是启动了两个线程的,分别调用两个方法,这个大伙都能看懂没问题.
问题就在这两个方法里边
getUserData()方法里边一开始是 synchronized  同步testData1对象的,这个也没问题,然后延时再同步testData2对象  看起来也没问题,    错! 你看问题怎么只看一个地方的? 另一个线程干了啥?我们一起来看看
getUserData2()方法看上去跟方法getUserData()差不多,  但是注意看,重点来了下面的线程也就是执行getUserData2()的线程里边  synchronized   同步的对象先是 testData2 然后延时再同步testData1  刚好跟上面一个线程对着干了
可能你这会说那又有什么关系
额........这个就要从java的执行顺序说起了,两个线程都执行了start  那线程就会起来,不存在谁等等谁再起来的说法  也就是说程序一跑到这里会执行这两个线程然后各自执行自己线程里边的方法   
第一个线程同步 synchronized  testData1  然后等待 3秒   
第二个线程同步 synchronized  testData2  然后等待3秒
开始计时 3秒过后 
第一个线程继续走 synchronized  testData2  重点来了,上面说到testData2这会正在被第二个线程 synchronized  同步      于是只能等待
第二个线程继续走 synchronized  testData1 重点又来了,上面说到 testData1这会正在被第一个线程 synchronized   同步   于是只能等待

看到没有线程走到这里就处于等待状态了,它又没注销又没继续往下走,你说奇怪不奇怪  正常逻辑三秒后总共会看到四条打印 而实际只会看到两条打印   害 ,这就是传说中的死锁   
(这里再说一点个人的理解可以忽略   就是一旦上锁当前线程事件没处理完锁都不会释放的)

那么问题又来了,竟然有死锁,那下次我自己写这个锁出现了死锁怎么办,怎么解决呢,好问题,请往下看:
博主我也不懂,怎么搞,百度啊,傻缺,难道干等着这个问题一直占用你脑袋的内存资源吗,不,不存在的,有垃圾就给它清掉
于是我就查了一下如何解决 死锁 ,果不其然看到了一博主写的还不错,于是我就搬过来按照我的理解重新梳理了一遍如下
产生死锁的时候下面这个四种情况就会统统存在
1.synchronized  对象互斥    -------->回看上面的死锁例子  满足   testData1 和 testData2
2.存在对象被占有且等待       -------->回看上面的死锁例子  满足    线程一启动各自占用了一个不同的对象且等待
3.不可抢占                          ----------->回看上面的死锁例子  满足    synchronized  关键字持有对象后该 同步的动作还没执行完是会一直持有且不可抢占的

4.循环等待                          ----------->回看上面的死锁例子  满足   各自的线程内对象的持有进行第二个 synchronized  同步的时候都出现了等待的情况

也就是说少于这四个条件就不会存在死锁问题,那么我们就来对它进行搞破坏就行了,或者每次写这种同步问题时都进行一次破坏就不会出现死锁了
那么就来吧,相信读书的时候搞破坏你们是最在行的,嘿嘿

互斥这个玩意是没办法解决了,你注意看两个线程都在相互引用对应的 testData1 和 testData2 对象
那么我们从 2.对象被占有且等待   这个条件入手

既然对象被占有那下一个线程进去的时候先让它等等不就可以了?   好主意,于是就有下面的一个方法(你可能会说跟你想象的不一样啊,答案不是唯一的,不要死钻牛角尖,你去试验你的想法能解决问题就行了哈,同学,不小心摸了摸你的小脑袋)
我们让线程执行的时候先去判断这些个对象是否已经被占有   
如何知道它们是否被占有呢 

oh  有了,当任意一个线程进来的时候  我们用一个List列表去记录它需要持有的 synchronized  对象

var arrayList1: ArrayList<TestData> = ArrayList()

如果不存在就放入列表当占有对象的线程处理完成事件后主动移除 ,于是就实现对象占有标记如下图的getAllSync()方法
 

private fun getAllSync( data1: TestData, data2: TestData): Boolean {
    synchronized(arrayList1){
        if(!arrayList1.contains(data1) && !arrayList1.contains(data2)){
            arrayList1.add(data1)
            arrayList1.add(data2)
            return true
        }
        return false
    }
}

private fun removeAllSync(data1: TestData, data2: TestData){
    synchronized(arrayList1){
        arrayList1.remove(data1)
        arrayList1.remove(data2)
    }
}


执行效果

与预期一致

我们再来看看第三个条件有没有可能进行破坏

3.不可抢占  ???????  来我们来看看上边我写了啥

我擦,这不是为难我程序员吗? 别慌,既然synchronized  必定会被占用的   那么我们不用他不就行了,那不用他我们怎么同步 啊,你在搞事情!!!!!!!!!!!!!!!
既然 synchronized  是上锁同步 那有没有别的  API  函数也可以进行上锁同步呢,google 爸爸  肯定也想到了这一点,那就是ReentrantLock 这个也是进行上锁使用的
ok ,那么来,我们用它来实现上锁,然后我们就可以远离死锁了
ReentrantLock  这玩意怎么用? 问我?我也不会用啊,不过这里好像有个坑先跳过改天研究


4.破坏循环等待 
对需要上锁的对象进行编号,让其按从大到小执行或者从小到大执行

运行效果

符合预期,好了,这期就先到这里了,眼睛特别困,要去睡一会了,如果觉得有用不要忘记点赞加收藏哦  ^ _ ^ _!

关于一些其他衍生的同步锁

ReentranLock

​​​​​​Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock) - 那些年的事儿 - 博客园




 


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值