Thread{ synchronized(result){ result.s = currentThread().name result.printResult() } }.apply { name = “first thread” start() }
Thread{ synchronized(result){ result.s = currentThread().name result.printResult() } }.apply { name = “second thread” start() }
}
输出结果:
1622795509253 ��first thread 1622795514269 ��second thread //时间相差5s,说明锁生效了
以上就是对Result类对象加锁,如果Result类有多个实例对象怎么办?我们稍微修改下调用方法:
fun testClassSync() { val result= Result()
//Result类的多个实例对象 val result2= Result()
Thread{ synchronized(result){ result.s = currentThread().name result.printResult() } }.apply { name = “first thread” start() }
Thread{ synchronized(result2){ result2.s = currentThread().name result2.printResult() } }.apply { name = “second thread” start() }
}
创建了多个Result类对象,first线程对A对象加锁,并不会影响second线程获取B对象锁,输出结果:
1622795623480 ��first thread 1622795623480 ��second thread
如果我们对Result类加锁,那所有的对象将会共享同一锁:
fun testClassSync() { val result= Result() val result2= Result()
//对Result类加锁 Thread{ synchronized(Result::class.java){ result.s = currentThread().name result.printResult() } }.apply { name = “first thread” start() }
Thread{ synchronized(Result::class.java){ result2.s = currentThread().name result2.printResult() } }.apply { name = “second thread” start() }
}
输出结果:
1622796124320 ��first thread 1622796129336 ��second thread
尽管first线程和second线程调用的是不同对象,但是first线程对Resutl类加上了类锁,second线程只能乖乖等着。
输出结果表示:对xx::class.java加上类锁的时候,该类所有的对象将会共享同一锁。
2.Lock
lock是一个接口,我们常用的实现类有ReentrantLock,意思是可重入锁。
我们定义一个全局变量,在两个线程中同时进行写操作,同时start两个线程
fun testLockSync() {
var count = 0
val thread1 = Thread{ for (i in 0…1000){ count += i } println(“
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
:
c
o
u
n
t
:
{Thread.currentThread().name} : count:
Thread.currentThread().name:count:{count}”) }
val thread2 = Thread{ for (i in 0…1000){ count += i } println(“
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
:
c
o
u
n
t
:
{Thread.currentThread().name} : count:
Thread.currentThread().name:count:{count}”) }
//同时开启两个线程 thread1.start() thread2.start() }
输出结果:会发现每次输出的结果都不一样
Thread-1 : count:505253 Thread-2 : count:1001000
Thread-2 : count:1001000 Thread-1 : count:1001000
Thread-2 : count:822155 Thread-1 : count:822155
使用ReentrantLock保证线程安全:
fun testLockSync() {
val lock = ReentrantLock() var count = 0
val thread1 = Thread{ lock.lock()
for (i in 0…1000){ count += i } println(“
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
:
c
o
u
n
t
:
{Thread.currentThread().name} : count:
Thread.currentThread().name:count:{count}”)
lock.unlock() }
val thread2 = Thread{ lock.lock()
for (i in 0…1000){ count += i } println(“
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
:
c
o
u
n
t
:
{Thread.currentThread().name} : count:
Thread.currentThread().name:count:{count}”)
lock.unlock() }
thread1.start() thread2.start() }
输出结果和执行顺序每次都是一样:
Thread-1 : count:500500 Thread-2 : count:1001000
Lock常用方法
lock() :获取锁,获取成功设置当前线程count++,如果其他线程占有锁,则当前线程不可用,等待。tryLock() : 获取锁,如果锁不可用,则此方法将立即返回,线程不阻塞,可设置等待时间。unLock() : 尝试释放锁,当前thread count–,如果count为0,则释放锁。
2.Object
在kotlin中object有很多作用,可以实现对象表达式、对象声明等
对象表达式就是创建一个Object类,实现单例模式:
object Singleton {
}
在idea中查看其反编译java代码:
public final class Singleton { public static final Singleton INSTANCE;
private Singleton() { }
static { Singleton var0 = new Singleton(); INSTANCE = var0; } }
可以看到其属于饿汉模式: 优点是在类加载的时候创建,不会存在线程安全问题,效率高。缺点就是浪费资源。具体可以参考此链接
但是在Object类中定义的方法并不是线程安全的
我们定义如下方法:
object Singleton {
fun printS(){ println(“${System.currentTimeMillis()} :
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
"
)
s
l
e
e
p
(
1000
)
p
r
i
n
t
l
n
(
"
{Thread.currentThread().name}") sleep(1000) println("
Thread.currentThread().name")sleep(1000)println("{System.currentTimeMillis()} : ${Thread.currentThread().name}”) } }
调用:
fun testObjectSync() {
val thread1 = thread(start = false,name = “first thread”){ Singleton.printS() }
val thread2 = thread(start = false,name = “second thread”){ Singleton.printS() }
thread1.start() thread2.start() }
输出:
1623036691322 : first thread 1623036691322 : second thread 1623036692329 : second thread 1623036692329 : first thread
我们发现两个线程能够同时获取到对象,输出结果几乎是同一时间,并没有起到sleep效果。
还是需要加锁@Synchronized才能实现线程安全,如下:
object Singleton {
@Synchronized fun printS(){ println(“${System.currentTimeMillis()} :
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
"
)
s
l
e
e
p
(
1000
)
p
r
i
n
t
l
n
(
"
{Thread.currentThread().name}") sleep(1000) println("
Thread.currentThread().name")sleep(1000)println("{System.currentTimeMillis()} : ${Thread.currentThread().name}”) }
}
输出: 1623036783474 : first thread 1623036784474 : first thread 1623036784474 : second thread 1623036785476 : second thread
3.by lazy实现
Object虽然非常简单是实现单例模式,但是Objcet不能初始化参数,我们可以利用by lazy 的延迟加载属性创建带参数的单例类:
class SomeSingleton(s:String) {
companion object{
private var s = “”
private val instance : SomeSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SomeSingleton(s) }
fun getInstances(string: String):SomeSingleton{ this.s = string return instance } }
fun printS(){ println(“${System.currentTimeMillis()} :
T
h
r
e
a
d
.
c
u
r
r
e
n
t
T
h
r
e
a
d
(
)
.
n
a
m
e
−
−
−
−
−
−
−
s
i
s
:
{Thread.currentThread().name} ------- s is:
Thread.currentThread().name−−−−−−−sis:{s}”) Thread.sleep(5000) }
}
注意这里的by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED)
延迟属性的在初始化的过程分多钟情况,首先是默认的SYNCHRONIZED,上锁为了保证只有一条线程可去初始化lazy属性。也就是说同时多线程进行访问该延迟属性时,如果没有初始化好,其他线程将无法访问。
延迟加载上锁实现源码:
private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable { private var initializer: (() -> T)? = initializer @Volatile private var _value: Any? = UNINITIALIZED_VALUE // final field is required to enable safe publication of constructed instance private val lock = lock ?: this
override val value: T get() { val _v1 = _value if (_v1 !== UNINITIALIZED_VALUE) { @Suppress(“UNCHECKED_CAST”) return _v1 as T }
return synchronized(lock) { val _v2 = _value if (_v2 !== UNINITIALIZED_VALUE) { @Suppress(“UNCHECKED_CAST”) (_v2 as T) } else { val typedValue = initializer!!() _value = typedValue initializer = null typedValue } } }
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else “Lazy value not initialized yet.”
private fun writeReplace(): Any = InitializedLazyImpl(value) }
同样SomeSingleton中的方法并不是线程安全的,同样需要加上@Synchorized,这里就不过多叙述了。
4.协程中的线程安全
协程中提供了Mutex来保证互斥,可以看做是Synchorinzed和Lock的替代品,还有withLock 扩展函数,可以⽅便替代常⽤的:
mutex.lock() try { //do something }finally { mutex.unlock() }
替换为:
mutex.withLock { //do something }
具体源码:
@OptIn(ExperimentalContracts::class) public suspend inline fun Mutex.withLock(owner: Any? = null, action: () -> T): T { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
lock(owner) try { return action() } finally { unlock(owner) } }
我们看一下具体的使用:
suspend fun testMutex() { var count = 0
val job1 = CoroutineScope(Dispatchers.IO).launch{ repeat(100){ count ++ //delay 1ms是为了比避免执行太快 delay(1) } println(“count1:${count}”) }
val job2 = CoroutineScope(Dispatchers.IO).launch{ repeat(100){ count ++ delay(1) } println(“count2:${count}”) }
job1.join() job2.join() }
我们多次运行看下结果,发现每次输出都不一样:
count2:196 count1:196
我们加上Mutex试一下:注意,对于多个协程来说用的是同一个Mutex
suspend fun testMutex() { var count = 0
//注意:对于多个协程来说用的是同一个Mutex val mutex = Mutex()
val job1 = CoroutineScope(Dispatchers.IO).launch{
mutex.withLock(count) { repeat(100) { count++ delay(1) } }
println(“count1:${count}”) }
val job2 = CoroutineScope(Dispatchers.IO).launch{
mutex.withLock(count) { repeat(100) { count++ delay(1) } }
println(“count2:${count}”) }
job1.join() job2.join() }
输出结果:几乎同时开启两个协程,去竞争count的锁,job1和job2谁先拿到count的锁几率是相同的
count2:100 count1:200
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
一、网安学习成长路线图
网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、网安视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
三、精品网安学习书籍
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、网络安全源码合集+工具包
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、网络安全面试题
最后就是大家最关心的网络安全面试题板块
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
cda342f496f9276a4cda5fcf.png)
四、网络安全源码合集+工具包
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、网络安全面试题
最后就是大家最关心的网络安全面试题板块
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长! [外链图片转存中…(img-3STXqmWi-1712923009888)]
精彩链接
发表评论