線程,進(jìn)程和協(xié)程
線程
線程的概念
并發(fā)
任務(wù)數(shù)大于cpu核載,通過(guò)系統(tǒng)的各種任務(wù)跳讀算法,是任務(wù)“在一起”執(zhí)行任務(wù)唬党! 假的多任務(wù)
并行
任務(wù)數(shù)小于cpu核數(shù)鹃共,即任務(wù)真的在一起執(zhí)行
多線程
1 同時(shí)執(zhí)行
下面例子中test1和test2是同時(shí)執(zhí)行
import threading
import time
def tes1():
for i in range(3):
print("--test1--%d" % i)
time.sleep(1)
def tes2():
for i in range(3):
print("--test2--%d" % i)
time.sleep(1)
if __name__ == "__main__":
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start() # 啟動(dòng)線程,讓線程開(kāi)始執(zhí)行
t2.start()
# 執(zhí)行結(jié)果
--test1--0
--test2--0
(間隔1秒)
--test1--1
--test2--1
(間隔1秒)
--test1--2
--test2--2
2 順序執(zhí)行
test1先執(zhí)行,test2后執(zhí)行
import threading
import time
def tes1():
for i in range(3):
print("--test1--%d" % i)
def tes2():
for i in range(3):
print("--test2--%d" % i)
if __name__ == "__main__":
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
time.sleep(1)
print("test1 is over")
t2.start()
time.sleep(1)
print("test2 is over")
# 執(zhí)行結(jié)果
--test1--0
--test1--1
--test1--2
(間隔1秒)
test1 over
--test2--0
--test2--1
--test2--2
(間隔1面)
test2 over
多線程全局變量
全局變量
import threading
import time
g_num = 100
def test1():
global g_num
g_num += 1
print("--in test1 g_num=%d" % g_num)
def test2():
print("--in test2 g_num=%d" % g_num)
if __name__ == "__main__":
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
time.sleep(1)
t2.start()+
time.sleep(1)
去函數(shù)那個(gè)筆記了解全局變量
多線程全局變量實(shí)參
import threading
import time
g_num = 100
def test1(temp): #傳遞實(shí)參的方式
temp.append(33)
print("--in test1 temp=%s" % str(temp))
545.
def test2():
print("--in test2 temp=%s" % str(temp))
g_num = [11,22]
if __name__ == "__main__":
# 創(chuàng)建線程
t1 = threading.Thread(target=test1, args=(g_num,))
t2 = threading.Thread(target=test2, args=(g_num,))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
多線程共享全局變量資源競(jìng)爭(zhēng)
import threading
import time
g_num = 0
def test1():
glibal g_num
for i in range(num):
g_num += 1
print("--in test1 g_num=%d" % g_num)
def test2():
glibal g_num
for i in range(num):
g_num += 1
print("--in test2 g_num=%d" % g_num)
if __name__ == "__main__":
# 創(chuàng)建線程
t1 = threading.Thread(target=test1,args=(100,))
t2 = threading.Thread(target=test2,args=(100,))
# t1 = threading.Thread(target=test1,args=(10000,))
# t2 = threading.Thread(target=test2,args=(10000,))
t1.start()
t2.start()
多線程解決全局變量資源競(jìng)爭(zhēng)01
使用互斥鎖
import threading
import time
g_num = 0
def test1():
glibal g_num
# 上鎖,如果之前沒(méi)有上鎖,那么此時(shí)上鎖成功
# 如果之前上過(guò)鎖,那么就會(huì)堵塞在這里,知道這個(gè)鎖解開(kāi)為止
muext.acquire()
for i in range(num):
g_num += 1
# 解鎖
muext.release()
print("--in test1 g_num=%d" % g_num)
def test2():
glibal g_num
muext.acquire()
for i in range(num):
g_num += 1
muext.release()
print("--in test2 g_num=%d" % g_num)
# 建立一個(gè)互斥鎖,默認(rèn)是沒(méi)有上鎖的
muext = threading.Lock()
if __name__ == "__main__":
t1 = threading.Thread(target=test1,args=(10000,))
t2 = threading.Thread(target=test2,args=(10000,))
t1.start()
t2.start()
# 上述代碼上鎖和解鎖在for循環(huán)外,解釋:子線程t1和t2不知道誰(shuí)先執(zhí)行,假如是t1先執(zhí)行,在上鎖的后,一直執(zhí)行for循環(huán)驶拱,知道循環(huán)結(jié)束為止然后在解鎖霜浴,在執(zhí)行t2此時(shí)全局變量從0變成了10000,直到for循環(huán)結(jié)束解鎖變成2000,程序解鎖
多線程解決全局變量資源競(jìng)爭(zhēng)02
import threading
import time
g_num = 0
def test1():
glibal g_num
for i in range(num):
muext.acquire()
g_num += 1
muext.release()
print("--in test1 g_num=%d" % g_num)
def test2():
glibal g_num
for i in range(num):
muext.acquire()
g_num += 1
muext.release()
print("--in test2 g_num=%d" % g_num)
muext = threading.Lock()
if __name__ == "__main__":
t1 = threading.Thread(target=test1,args=(10000,))
t2 = threading.Thread(target=test2,args=(10000,))
t1.start()
t2.start()
# 如果上鎖和解鎖在for循環(huán)內(nèi)部蓝纲,不管t1和t2誰(shuí)先執(zhí)行阴孟,每次執(zhí)行+1結(jié)束后解鎖,然后在分配t1和t2誰(shuí)先執(zhí)行驻龟,這個(gè)先后是沒(méi)有規(guī)律的,可能是t1執(zhí)行很多次之后再是執(zhí)行t2温眉,可能反之,所以其中一個(gè)是for循環(huán)執(zhí)行結(jié)束后得到20000,但是另外一個(gè)一定是執(zhí)行10000后還在疊加
多任務(wù)版UDP聊天
import socket
import threading
if __name__ == "__main__":
# 創(chuàng)建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定端口
udp_socket.bind(("", 7788))
# 獲得對(duì)方ip和port
dest_ip = input("請(qǐng)輸入ip:")
dest_port = input("請(qǐng)輸入port:")
# 接受數(shù)據(jù)
while True:
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
# 發(fā)送數(shù)據(jù)
while True:
send_data = input("輸入數(shù)據(jù):")
udp_socket.sendto(send_data.encode("utf-8"), dest_ip,dest_port)
# 利用多線程
import socket
import threading
def recv_msg(udp_socket):
# 接受數(shù)據(jù)
while True:
recv_data = udp_socket.recvfrom(1024)
#print(recv_data)
print("[%s]:%s" %(recv_data[1], str(recv_data[0].decode("utf-8"))))
# 顯示發(fā)送的對(duì)方地址和信息
def send_msg(udp_socket,dest_ip,dest_port):
# 發(fā)送數(shù)據(jù)
while True:
send_data = input("輸入數(shù)據(jù):")
udp_socket.sendto(send_data.encode("utf-8"), dest_ip,dest_port)
if __name__ == "__main__":
# 創(chuàng)建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定端口
udp_socket.bind(("", 7788))
# 獲得對(duì)方ip和port
dest_ip = input("請(qǐng)輸入ip:")
dest_port = int(input("請(qǐng)輸入port:")) # 注意
t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
t_send = threading.Thread(target=send_msg, args=(udp_socket,dest_ip,dest_port))
t_recv.start()
t_send.start()
進(jìn)程
進(jìn)程的概念
一個(gè)程序運(yùn)行起來(lái),代碼+用到的資源稱之為進(jìn)程翁狐,他是操作系統(tǒng)分配資源的基本單位元
導(dǎo)入multiprocessing模塊
子進(jìn)程的傳遞參數(shù)
import multiprocessing
# import os # 導(dǎo)入路徑模塊
import time
def test(a,b,c,*args, **kwargs):
print(a)
print(b)
print(c)
print(args) # 拆包(元組)
print(kwargs) # 拆包(字典)
if __name__ == "__main__":
p = multiprocessing.Process(target=test, args=(1,2,3,4,5,6,7,8), kwargs={"name":"Lily"})
# 當(dāng)導(dǎo)入元素和元組的時(shí)候类溢,統(tǒng)一以元組的形式導(dǎo)入
# 當(dāng)傳入kwargs的時(shí)候,在創(chuàng)建p類的時(shí)候,傳入字典形式
p.start()
多進(jìn)程之間不共享全局變量
進(jìn)程之間是兩個(gè)獨(dú)立的程序不共享全局變量
import multiprocessing
import time
nums = [1,2,3] #設(shè)定全局變量
def test1():
nums.append(4) # 利用方法改變?nèi)肿兞? print("在test1中nums=%s" % str(nums))
def test2():
print("在test2中nums=%s" % str(nums))
if __name__ == "__main__":
# 創(chuàng)建進(jìn)程
p1 = multiprocessing.Proess(target=test1)
p2 = multiprocessing.Proess(target=test2)
p1.start()
p1.join() # 確保p1在執(zhí)行結(jié)束后再執(zhí)行p2
time.sleep(1) # 或者是在p1執(zhí)行后停頓1秒
p2.start()
# 得出結(jié)果是test1中[1,2,3,4], test2中[1,2,3]
# 在進(jìn)程中不共享全局變量
進(jìn)程和線程的區(qū)別
簡(jiǎn)單的對(duì)比
進(jìn)程:能夠完成多任務(wù),例如一臺(tái)電腦上可以運(yùn)行多個(gè)QQ
(進(jìn)程是系統(tǒng)進(jìn)行資源的分配和調(diào)度的一個(gè)獨(dú)立單位)
線程:能夠完成多任務(wù),例如在同一個(gè)QQ可以開(kāi)多個(gè)聊天窗口
(線程屬于進(jìn)程,是進(jìn)程的一個(gè)實(shí)體,是cpu調(diào)度和分配的基本單位,能更小的獨(dú)立運(yùn)行的基本單位露懒,線程自己使用系統(tǒng)資源)
區(qū)別
1 一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程
2 線程的劃分尺度小于進(jìn)程(就是占用資源比進(jìn)程少)
3 進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元闯冷,而多個(gè)線程共享內(nèi)存,提高工作效率
4 線程不能獨(dú)立的運(yùn)行,必須依存在進(jìn)程中
5 進(jìn)程就好比工廠的流水線,線程就好比流水線的員工
進(jìn)程之間的通信-Queue隊(duì)列
實(shí)例(僅限一臺(tái)電腦或是服務(wù)器中的兩個(gè)進(jìn)程之間的數(shù)據(jù)共享)
import multiprocessing
# 一個(gè)進(jìn)程向Queue中寫入數(shù)據(jù),另一個(gè)進(jìn)程從Queue獲取數(shù)據(jù)
# 通過(guò)Queue完成了多歌需要配合進(jìn)程間的數(shù)據(jù)共享
def down(q):
# 下載數(shù)據(jù)
# 模擬在網(wǎng)上下載數(shù)據(jù)懈词,就簡(jiǎn)單的創(chuàng)建下載好的列表
data = [1,2,3,4]
# 向隊(duì)列寫入數(shù)據(jù)
for temp in data:
q.put(temp)
print("--數(shù)據(jù)已經(jīng)存到隊(duì)列中---")
def fenxi(q):
# 數(shù)據(jù)處理
fenxi_data = list() #或者是fenxi_list = []
# 向保存好的隊(duì)列中獲取數(shù)據(jù)
while True:
new_data = q.get() # new_data是獲取的數(shù)據(jù)
fenxi_data.append(new_data)
# 判斷:如果隊(duì)被取空的話,就退出
if q.empty():
break
if __name__ == "__main__":
# 創(chuàng)建一個(gè)隊(duì)列
q = multiprocessing.Queue()
# 創(chuàng)建進(jìn)程 # 傳入實(shí)參隊(duì)列在創(chuàng)建的子進(jìn)程中調(diào)用q
p1 = multiprocessing.Process(target=down, args=(q,))
p2 = muultiprocessing.Proess(target=fenxi, args=(q,))
p1.start()
# p1.join()
p2.start()
# 以上操作有問(wèn)題,創(chuàng)建的子進(jìn)程p1和p2蛇耀,不能確定那個(gè)子進(jìn)程先運(yùn)行,會(huì)導(dǎo)致p1還沒(méi)有下載好坎弯,p2就直接獲取數(shù)據(jù)纺涤,所以在p1.start()后添加一個(gè)p1.join(), p1.join()的功能就是讓p1執(zhí)行完之后再執(zhí)行其他的進(jìn)程
補(bǔ)充說(shuō)明
初始化Queue()對(duì)象時(shí)(例如q=Queue()),有下列方法
q.put() # 向隊(duì)列導(dǎo)入寫入數(shù)據(jù)
q.get() # 向隊(duì)列下載獲取數(shù)據(jù)
q.empty() # 如果隊(duì)列是空的,返回True, 反之False
q.full() # 如果列隊(duì)滿了,返回True,反之False
進(jìn)程池
進(jìn)程之間的通信
進(jìn)程間通信就是在不同進(jìn)程之間傳播或交換信息抠忘,那么不同進(jìn)程之間存在著什么雙方都可以訪問(wèn)的介質(zhì)呢撩炊?進(jìn)程的用戶空間是互相獨(dú)立的,一般而言是不能互相訪問(wèn)的崎脉,唯一的例外是共享內(nèi)存區(qū)拧咳。但是,系統(tǒng)空間卻是“公共場(chǎng)所”囚灼,所以內(nèi)核顯然可以提供這樣的條件骆膝。除此以外,那就是雙方都可以訪問(wèn)的外設(shè)了灶体。在這個(gè)意義上阅签,兩個(gè)進(jìn)程當(dāng)然也可以通過(guò)磁盤上的普通文件交換信息,或者通過(guò)“注冊(cè)表”或其它數(shù)據(jù)庫(kù)中的某些表項(xiàng)和記錄交換信息赃春。廣義上這也是進(jìn)程間通信的手段愉择,但是一般都不把這算作“進(jìn)程間通信”。因?yàn)槟切┩ㄐ攀侄蔚男侍土耍藗儗?duì)進(jìn)程間通信的要求是要有一定的實(shí)時(shí)性锥涕。
進(jìn)程間通信主要包括管道, 系統(tǒng)IPC(包括消息隊(duì)列,信號(hào)量,共享存儲(chǔ)), SOCKET.
進(jìn)程和線程區(qū)別
定義的區(qū)別:進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立的單位衷戈;線程是進(jìn)程的實(shí)體,是cpu調(diào)度和分配的基本單位
一個(gè)程序至少有一個(gè)進(jìn)程层坠,一個(gè)進(jìn)程至少有一個(gè)線程;
線程的劃分尺度小于進(jìn)程(資源比進(jìn)程少)殖妇,使得多線程程序的并發(fā)性高。
進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元破花,而多個(gè)線程共享內(nèi)存谦趣,從而極大地提高了程序的運(yùn)行效率
線線程不能夠獨(dú)立執(zhí)行,必須依存在進(jìn)程中
可以將進(jìn)程理解為工廠中的一條流水線座每,而其中的線程就是這個(gè)流水線上的工人
優(yōu)缺點(diǎn):線程執(zhí)行開(kāi)銷小前鹅,效率高,,但不利于資源的管理和保護(hù)峭梳;而進(jìn)程正相反舰绘。
協(xié)程
協(xié)程概念
用到更少的資源,:在一個(gè)線程中的某個(gè)函數(shù)葱椭,可以在任何地方保存當(dāng)前函數(shù)的一 些臨時(shí)變量等信息捂寿,然后切換到另外一個(gè)函數(shù)中執(zhí)行,注意不是通過(guò)調(diào)用函 數(shù)的方式做到的孵运,并且切換的次數(shù)以及什么時(shí)候再切換到原來(lái)的函數(shù)都由開(kāi) 發(fā)者自己確定
協(xié)程和線程的區(qū)別
在實(shí)現(xiàn)多任務(wù)時(shí), 線程切換從系統(tǒng)層面遠(yuǎn)不止保存和恢復(fù) CPU上下文這么簡(jiǎn) 單秦陋。 操作系統(tǒng)為了程序運(yùn)行的高效性每個(gè)線程都有自己緩存Cache等等數(shù) 據(jù),操作系統(tǒng)還會(huì)幫你做這些數(shù)據(jù)的恢復(fù)操作治笨。 所以線程的切換非常耗性 能驳概。但是協(xié)程的切換只是單純的操作CPU的上下,所以?秒鐘切換個(gè)上百 萬(wàn)次系統(tǒng)都抗的住旷赖。
如何實(shí)現(xiàn)協(xié)程
1 生成器實(shí)現(xiàn)簡(jiǎn)單的協(xié)程
import time
def f1():
while True:
print("---1----")
yield
time.sleep(1)
def f2():
while True:
print("---2----")
yield
time.sleep(1)
if __name__ == "__main__":
ff1 = f1()
ff2 = f2()
while True:
next(ff1)
next(ff2)
2 greenlet實(shí)現(xiàn)協(xié)程 (了解)
from greenlet import greenlet
import time
def w1():
while True:
print("---1---")
ww1.switch() # 使用greenlet模塊中swtich()方法
time.sleep(1)
def w2():
while True:
print("---1---")
ww2.switch() # 使用greenlet模塊中swtich()方法
time.sleep(1)
if __name__ == "__main__":
ww1 = greenlet(w1)
ww2 = greenlet(w2)
ww1.switch()
3 gevent實(shí)現(xiàn)協(xié)程(重要)
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
g1.join()
g2.join()
g3.join()
# 執(zhí)行結(jié)果
<Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 0
<Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 1
<Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 2
<Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 3
<Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 4
<Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 0
<Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 1
<Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 2
<Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 3
<Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 4
<Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 0
<Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 1
<Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 2
<Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 3
<Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 4
# 達(dá)成任務(wù)切換
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
# 用來(lái)模擬一個(gè)耗時(shí)操作抡句,不是用time模塊
gevent.sleep(1)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
g1.join()
g2.join()
g3.join()
# 執(zhí)行結(jié)果
<Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 0
<Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 0
<Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 0
<Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 1
<Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 1
<Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 1
<Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 2
<Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 2
<Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 2
<Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 3
<Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 3
<Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 3
<Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 4
<Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 4
<Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 4
進(jìn)程,線程和協(xié)程的對(duì)比(面試重點(diǎn))
A 進(jìn)程是資源分配的單位
B 線程是操作系統(tǒng)調(diào)度的單位
C 進(jìn)程切換需要的資源最大杠愧,效率低
D 線程切換需要的資源一般,效率也很一般
E 協(xié)程切換任務(wù)資源小逞壁,效率高
F 多進(jìn)程流济,多線程根據(jù)cpu的核數(shù)不一樣可能并行,但是協(xié)程是在一個(gè)線程中腌闯,所以是并發(fā)的
G 進(jìn)程不共享資源绳瘟,線程共享資源
GIL(全局解釋器鎖)(面試重點(diǎn))
每個(gè)線程在執(zhí)行過(guò)程中都需要先獲取GIL,保證同一時(shí)刻只有一個(gè)線程可以執(zhí)行代碼姿骏,所以線程是并發(fā)的糖声,都是講并發(fā)運(yùn)行成串行,由此來(lái)控制同一時(shí)間內(nèi)共享數(shù)據(jù)只能被一個(gè)任務(wù)修改,進(jìn)而保證數(shù)據(jù)的安全!
底層知識(shí)
因?yàn)閜ython的線程是調(diào)用操作系統(tǒng)的原生線程蘸泻,這個(gè)原生線程就是C語(yǔ)言寫的原生線程琉苇。因?yàn)閜ython是用C寫的,啟動(dòng)的時(shí)候就是調(diào)用的C語(yǔ)言的接口悦施。因?yàn)閱?dòng)的C語(yǔ)言的遠(yuǎn)程線程并扇,那它要調(diào)這個(gè)線程去執(zhí)行任務(wù)就必須知道上下文,所以python要去調(diào)C語(yǔ)言的接口的線程抡诞,必須要把這個(gè)上限問(wèn)關(guān)系傳給python穷蛹,那就變成了一個(gè)在加減的時(shí)候要讓程序串行才能一次計(jì)算。就是先讓線程1昼汗,再讓線程2.......
多線程用于IO密集型肴熏,如socket,爬蟲(chóng)顷窒,web
多進(jìn)程用于計(jì)算密集型蛙吏,如金融分析
互斥鎖(面試重點(diǎn))
當(dāng)多個(gè)線程幾乎同時(shí)修改一個(gè)共享數(shù)據(jù)的時(shí)候,需要進(jìn)行同步控制蹋肮,線程同步能夠保證多個(gè)線程安全的競(jìng)爭(zhēng)資源出刷,最簡(jiǎn)單的同步機(jī)制就是引入互斥鎖
如何運(yùn)行:某個(gè)線程需要更改共享數(shù)據(jù)的時(shí)候,先鎖定坯辩,此時(shí)資源狀態(tài)為鎖定狀態(tài)馁龟,其他線程不能更改,直到該線程釋放資源漆魔,將資源的狀態(tài)變成非鎖定狀態(tài)坷檩,其他線程才能再次鎖定該資源,互斥鎖保證每次只有一個(gè)線程進(jìn)行操作改抡,從而保證多線程情況的數(shù)據(jù)正確性!
優(yōu)點(diǎn):確保某段關(guān)鍵代碼只能有一個(gè)線程從頭到尾完整的執(zhí)行
缺點(diǎn):A--阻止了多線程的并發(fā)矢炼,包含鎖的某段代碼只能以單線程的模式執(zhí)行,效率大打折扣阿纤。B--由于可以存在多個(gè)鎖句灌,不同的線程持有不同的鎖,并試圖獲取對(duì)方的鎖欠拾,可能會(huì)造成死鎖胰锌!