面试题答案
一键面试资源竞争问题及举例
- 数据不一致:
- 例如有一个共享变量
count
,初始值为 0。两个线程Thread1
和Thread2
都要对其进行加 1 操作。假设Thread1
读取count
的值为 0,此时线程调度切换到Thread2
,Thread2
也读取count
的值为 0 。然后Thread1
对count
加 1 ,count
变为 1 ,接着Thread2
也对count
加 1 ,count
还是 1 ,而预期结果应该是 2 ,这就导致了数据不一致。
- 例如有一个共享变量
- 死锁:
- 假设有两个资源
ResourceA
和ResourceB
,Thread1
已经获取了ResourceA
,并尝试获取ResourceB
;同时Thread2
已经获取了ResourceB
,并尝试获取ResourceA
。此时两个线程相互等待对方释放资源,形成死锁,导致程序无法继续执行。
- 假设有两个资源
解决策略
- 互斥锁(Mutex):
- 使用互斥锁可以保证在同一时间只有一个线程能够访问共享资源。例如在上述
count
变量的例子中,可以定义一个互斥锁mutex
。当Thread1
要对count
进行操作时,先获取mutex
,操作完成后释放mutex
。在Thread1
获取mutex
期间,Thread2
无法获取mutex
,也就不能操作count
,从而避免数据不一致问题。
- 使用互斥锁可以保证在同一时间只有一个线程能够访问共享资源。例如在上述
- 读写锁(Read - Write Lock):
- 适用于读多写少的场景。例如有一个共享的数据结构,多个线程可能读取该数据结构,而只有少数线程会对其进行写操作。使用读写锁时,读操作可以并发执行,因为读操作不会修改数据,不会产生数据不一致问题。而写操作时,需要先获取写锁,此时其他读线程和写线程都不能操作,保证写操作的原子性,避免数据不一致。
- 信号量(Semaphore):
- 可以控制同时访问共享资源的线程数量。例如有一个数据库连接池,连接池中的连接数量有限,为
N
。可以使用信号量来控制同时使用数据库连接的线程数量不超过N
。当一个线程要获取数据库连接时,先获取信号量,如果信号量计数大于 0 ,则获取成功,信号量计数减 1 ;当线程使用完连接归还时,信号量计数加 1 。这样可以避免过多线程同时竞争有限的数据库连接资源。
- 可以控制同时访问共享资源的线程数量。例如有一个数据库连接池,连接池中的连接数量有限,为
- 避免死锁的策略:
- 破坏死锁的四个必要条件:
- 互斥条件:尽量避免资源的独占使用,如果资源可以共享使用,则不会产生死锁。例如某些文件操作可以允许多个线程同时读。
- 占有并等待条件:要求线程一次性获取所有需要的资源,而不是获取部分资源后再等待其他资源。例如在上述死锁例子中,如果
Thread1
和Thread2
都尝试一次性获取ResourceA
和ResourceB
,就不会产生死锁。 - 不可剥夺条件:当一个线程获取了资源后,其他线程可以剥夺该线程的资源。但在实际应用中,这种方法实现起来较为复杂,可能影响程序的正确性。
- 循环等待条件:对资源进行排序,线程按照顺序获取资源。例如对
ResourceA
和ResourceB
进行编号,规定所有线程都先获取编号小的资源,再获取编号大的资源,这样可以避免循环等待,从而避免死锁。
- 破坏死锁的四个必要条件: