线程同步概念
根据一篇文章,精简了一下关于线程同步的知识
什么是线程同步?
当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),
所以我们用同步机制来解决这些问题。
为什么要同步?
1. 线程同步=线程排队
2. 多个线程访问共享的资源才需要同步
3. 多个线程读取常量不用同步,读取变量才要同步,即涉及线程要要对数据修改才同步
4. 多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。
如何线程同步?
1. 给共享资源的访问加同步锁(注意,为什么不是在每个对象里添加锁机制?没必要,因为同步是很耗资源的)
2. 同步锁是加在访问共享资源的代码段上的
3. 如果访问同一份共享资源,加的是不同的同步锁,并不起到同步的作用,且该同步锁没有任何意义,也就是关键要判断该锁是否是同一同步锁
4. 也就是说,同步锁本身也一定是多个线程之间的共享对象
5. 如函数体内部产生的同步锁,对于多线程不是同一个同步锁(具体看例子)
6. 任何一个Object Reference都可以作为同步锁
7. 是否是同一个同步锁,也就是看同步代码间,synchronized关键字是否使用同一个Object Reference,即同一个内存地址
8. 可以使用类的static final的属性对象作同步锁,使用final就是为了保证引用不要改变,保证同一个同步锁
9. 由于同步的范围越小越好,同步的代码块越少越好,所以不同的共享资源,要使用不同的同步锁,即缩小同步粒度
进程或线程同步互斥的控制方法
1.临界区(Critical Section):通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2.互斥量(Mutex):为协调共同对一个共享资源的单独访问而设计的。是一种信号量,常用来防止两个进程或线程在同一时刻访问相同的共享资源。
3.信号量(Semaphore):为控制一个具有有限数量用户资源而设计。
4.事件(Event):用来通知线程有一些事件已发生,从而启动后继任务的开始。
5.原子操作(Atomic operation):即不可分割开的操作;该操作一定是在同一个cpu时间片中完成,这样即使线程被切换,多个线程也不会看到同一块内存中不完整的数据
为了加深理解,下面举几个例子。
有两个采购员,他们的工作内容是相同的,都是遵循如下的步骤:
(1)到市场上去,寻找并购买有潜力的样品。
(2)回到公司,写报告。
这两个人的工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资源。所以,他们可以各自进行自己的工作,互不干扰。
这两个采购员就相当于两个线程;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。
下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。
这两个采购员有可能同时走到布告栏的前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏上写的信息。
下面增加一个角色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。
如果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观看修改后的信息。
如果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。
上述这两种情况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到,行政人员的工作流程和采购员的工作流程(执行代码)完全不同,
但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。