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)]

精彩链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。