程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java頂用戶向體系傳遞參數的三種根本方法實例分享

Java頂用戶向體系傳遞參數的三種根本方法實例分享

編輯:關於JAVA

Java頂用戶向體系傳遞參數的三種根本方法實例分享。本站提示廣大學習愛好者:(Java頂用戶向體系傳遞參數的三種根本方法實例分享)文章只能為提供參考,不一定能成為您想要的結果。以下是Java頂用戶向體系傳遞參數的三種根本方法實例分享正文


同步拜訪同享資本

在應用線程的時刻,一個很主要的成績是要防止多個線程對統一變量或其它資本的拜訪抵觸。一旦你略不留心,堆疊拜訪、在多個線程中修正(同享資本)等這些操作會招致各類各樣的成績;更嚴重的是,這些成績普通只會在比擬極端(好比高並發、臨盆辦事器、乃至在機能更好的硬件裝備上)的情形下才會湧現。
好比有如許一個情形:須要追蹤對一事宜處置的次數

counter = 0

def process_item(item):
  global counter
  ... do something with item ...
  counter += 1

假如你在多個線程中同時挪用這個函數,你會發明counter的值不是那末精確。在年夜多半情形下它是對的,但有時它會比現實的少幾個。
湧現這類情形的緣由是,計數增長操作現實上分三步履行:

  • 說明器獲得counter確當前值
  • 盤算新值
  • 將盤算的新值回寫counter變量

斟酌一下這類情形:在以後線程獲得到counter值後,另外一個線程搶占到了CPU,然後異樣也獲得到了counter值,並進一步將counter值從新盤算並完成回寫;以後時光片從新輪到以後線程(這裡僅作標識辨別,並不是現實以後),此時以後線程獲得到counter值照樣本來的,完成後續兩步操作後counter的值現實只加上1。
另外一種罕見情形是拜訪不完全或紛歧致狀況。這類情形重要產生在一個線程正在初始化或更新數據時,另外一個過程卻測驗考試讀取正在更改的數據。

原子操作
完成對同享變量或其它資本的同步拜訪最簡略的辦法是依附說明器的原子操作。原子操作是在一步完成履行的操作,在這一步中其它線程沒法取得該同享資本。
平日情形下,這類同步辦法只對那些只由單個焦點數據類型構成的同享資本有用,比方,字符串變量、數字、列表或許字典等。上面是幾個線程平安的操作:

  • 讀或許調換一個實例屬性
  • 讀或許調換一個全局變量
  • 從列表中獲得一項元素
  • 原位修正一個列表(例如:應用append增長一個列表項)
  • 從字典中獲得一項元素
  • 原位修正一個字典(例如:增長一個字典項、挪用clear辦法)

 留意,下面提到過,對一個變量或許屬性停止讀操作,然後修正它,終究將其回寫不是線程平安的。由於別的一個線程會在這個線程讀完卻沒有修正或回寫完成之前更改這個同享變量/屬性。

鎖是Python的threading模塊供給的最根本的同步機制。在任一時辰,一個鎖對象能夠被一個線程獲得,或許不被任何線程獲得。假如一個線程測驗考試去獲得一個曾經被另外一個線程獲得到的鎖對象,那末這個想要獲得鎖對象的線程只能臨時終止履行直到鎖對象被另外一個線程釋放失落。
鎖平日被用來完成對同享資本的同步拜訪。為每個同享資本創立一個Lock對象,當你須要拜訪該資本時,挪用acquire辦法來獲得鎖對象(假如其它線程曾經取得了該鎖,則以後線程需期待其被釋放),待資本拜訪完後,再挪用release辦法釋放鎖:

lock = Lock()

lock.acquire() #: will block if lock is already held
... access shared resource
lock.release()

留意,即便在拜訪同享資本的進程中失足了也應當釋放鎖,可以用try-finally來到達這一目標:

lock.acquire()
try:
  ... access shared resource
finally:
  lock.release() #: release lock, no matter what

在Python 2.5及今後的版本中,你可使用with語句。在應用鎖的時刻,with語句會在進入語句塊之前主動的獲得到該鎖對象,然後在語句塊履行完成後主動釋放失落鎖:

from __future__ import with_statement #: 2.5 only

with lock:
  ... access shared resource

acquire辦法帶一個可選的期待標識,它可用於設定當有其它線程占領鎖時能否壅塞。假如你將其值設為False,那末acquire辦法將不再壅塞,只是假如該鎖被占領時它會前往False:

if not lock.acquire(False):
  ... 鎖資本掉敗
else:
  try:
    ... access shared resource
  finally:
    lock.release()

你可使用locked辦法來檢討一個鎖對象能否已被獲得,留意不克不及用該辦法來斷定挪用acquire辦法時能否會壅塞,由於在locked辦法挪用完成到下一條語句(好比acquire)履行之間該鎖有能夠被其它線程占領。

if not lock.locked():
  #: 其它線程能夠鄙人一條語句履行之前占領了該鎖
  lock.acquire() #: 能夠會壅塞

簡略鎖的缺陷
尺度的鎖對象其實不關懷以後是哪一個線程占領了該鎖;假如該鎖曾經被占領了,那末任何其它測驗考試獲得該鎖的線程都邑被壅塞,即便是占領鎖的這個線程。斟酌一下上面這個例子:

lock = threading.Lock()


def get_first_part():
  lock.acquire()
  try:
    ... 從同享對象中獲得第一部門數據
  finally:
    lock.release()
  return data


def get_second_part():
  lock.acquire()
  try:
    ... 從同享對象中獲得第二部門數據
  finally:
    lock.release()
  return data

示例中,我們有一個同享資本,有兩個分離取這個同享資本第一部門和第二部門的函數。兩個拜訪函數都應用了鎖來確保在獲得數據時沒有其它線程修正對應的同享數據。
如今,假如我們想添加第三個函數來獲得兩個部門的數據,我們將會墮入泥潭。一個簡略的辦法是順次挪用這兩個函數,然後前往聯合的成果:

def get_both_parts():
  first = get_first_part()
  seconde = get_second_part()
  return first, second

這裡的成績是,若有某個線程在兩個函數挪用之間修正了同享資本,那末我們終究會獲得紛歧致的數據。最顯著的處理辦法是在這個函數中也應用lock:

  def get_both_parts():
    lock.acquire()
    try:
      first = get_first_part()
      seconde = get_second_part()
    finally:
      lock.release()
    return first, second

但是,這是弗成行的。外面的兩個拜訪函數將會壅塞,由於外層語句曾經占領了該鎖。為懂得決這個成績,你可以經由過程應用標志在拜訪函數中讓外層語句釋放鎖,但如許輕易掉去掌握並招致失足。榮幸的是,threading模塊包括了一個加倍適用的鎖完成:re-entrant鎖。
Re-Entrant Locks (RLock)

RLock類是簡略鎖的另外一個版本,它的特色在於,統一個鎖對象只要在被其它的線程占領時測驗考試獲得才會產生壅塞;而簡略鎖在統一個線程中同時只能被占領一次。假如以後線程曾經占領了某個RLock鎖對象,那末以後線程仍能再次獲得到該RLock鎖對象。

lock = threading.Lock()
lock.acquire()
lock.acquire() #: 這裡將會壅塞

lock = threading.RLock()
lock.acquire()
lock.acquire() #: 這裡不會產生壅塞

RLock的重要感化是處理嵌套拜訪同享資本的成績,就像後面描寫的示例。要想處理後面示例中的成績,我們只須要將Lock換為RLock對象,如許嵌套挪用也會OK.

lock = threading.RLock()


def get_first_part():
  ... see above


def get_second_part():
  ... see above


def get_both_parts():
  ... see above

如許既可以零丁拜訪兩部門數據也能夠一次拜訪兩部門數據而不會被鎖壅塞或許取得紛歧致的數據。
留意RLock會追蹤遞歸層級,是以記得在acquire落後行release操作。
Semaphores

旌旗燈號量是一個更高等的鎖機制。旌旗燈號量外部有一個計數器而不像鎖對象外部有鎖標識,並且只要當占用旌旗燈號量的線程數跨越旌旗燈號量時線程才壅塞。這許可了多個線程可以同時拜訪雷同的代碼區。

semaphore = threading.BoundedSemaphore()
semaphore.acquire() #: counter減小

... 拜訪同享資本

semaphore.release() #: counter增年夜

當旌旗燈號量被獲得的時刻,計數器減小;當旌旗燈號量被釋放的時刻,計數器增年夜。當獲得旌旗燈號量的時刻,假如計數器值為0,則該過程將壅塞。當某一旌旗燈號量被釋放,counter值增長為1時,被壅塞的線程(假如有的話)中會有一個得以持續運轉。
旌旗燈號量平日被用來限制對容量無限的資本的拜訪,好比一個收集銜接或許數據庫辦事器。在這類場景中,只須要將計數器初始化為最年夜值,旌旗燈號量的完成將為你完成剩下的工作。

max_connections = 10

semaphore = threading.BoundedSemaphore(max_connections)


假如你不傳任何初始化參數,計數器的值會被初始化為1.
Python的threading模塊供給了兩種旌旗燈號量完成。Semaphore類供給了一個無窮年夜小的旌旗燈號量,你可以挪用release隨意率性次來增年夜計數器的值。為了不毛病湧現,最好應用BoundedSemaphore類,如許當你挪用release的次數年夜於acquire次數時法式會失足提示。
線程同步

鎖可以用在線程間的同步上。threading模塊包括了一些用於線程間同步的類。
Events

一個事宜是一個簡略的同步對象,事宜表現為一個外部標識(internal flag),線程期待這個標識被其它線程設定,或許本身設定、消除這個標識。

event = threading.Event()

#: 一個客戶端線程期待flag被設定
event.wait()

#: 辦事端線程設置或許消除flag
event.set()
event.clear()

一旦標識被設定,wait辦法就不做任何處置(不會壅塞),當標識被消除時,wait將被壅塞直至其被從新設定。隨意率性數目的線程能夠會期待統一個事宜。
Conditions

前提是事宜對象的高等版本。前提表示為法式中的某種狀況轉變,線程可以期待給定前提或許前提產生的旌旗燈號。
上面是一個簡略的臨盆者/花費者實例。起首你須要創立一個前提對象:

#: 表現一個資本的從屬項
condition = threading.Condition()
臨盆者線程在告訴花費者線程有重生成資本之前須要取得前提:
#: 臨盆者線程
... 臨盆資本項
condition.acquire()
... 將資本項添加到資本中
condition.notify() #: 收回有可用資本的旌旗燈號
condition.release()
花費者必需獲得前提(和相干聯的鎖),然後測驗考試從資本中獲得資本項:
#: 花費者線程
condition.acquire()
while True:
  ...從資本中獲得資本項
  if item:
    break
  condition.wait() #: 休眠,直至有新的資本
condition.release()
... 處置資本

wait辦法釋放了鎖,然後將以後線程壅塞,直到有其它線程挪用了統一前提對象的notify或許notifyAll辦法,然後又從新拿到鎖。假如同時有多個線程在期待,那末notify辦法只會叫醒個中的一個線程,而notifyAll則會叫醒全體線程。
為了不在wait辦法處壅塞,你可以傳入一個超時參數,一個以秒為單元的浮點數。假如設置了超時參數,wait將會在指准時間前往,即便notify沒被挪用。一旦應用了超時,你必需檢討資本來肯定產生了甚麼。
留意,前提對象聯系關系著一個鎖,你必需在拜訪前提之前獲得這個鎖;異樣的,你必需在完成對前提的拜訪時釋放這個鎖。在臨盆代碼中,你應當應用try-finally或許with.
可以經由過程將鎖對象作為前提結構函數的參數來讓前提聯系關系一個曾經存在的鎖,這可以完成多個前提公用一個資本:

lock = threading.RLock()
condition_1 = threading.Condition(lock)
condition_2 = threading.Condition(lock)

互斥鎖同步
我們先來看一個例子:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, threading

# 假定這是你的銀行存款:
balance = 0
muxlock = threading.Lock()

def change_it(n):
  # 先存後取,成果應當為0:
  global balance
  balance = balance + n
  balance = balance - n

def run_thread(n):
  # 輪回次數一旦多起來,最初的數字就釀成非0
  for i in range(100000):
    change_it(n)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t3 = threading.Thread(target=run_thread, args=(9,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print balance

成果 :

[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
61
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
24

下面的例子引出了多線程編程的最多見成績:數據同享。當多個線程都修正某一個同享數據的時刻,須要停止同步掌握。
線程同步可以或許包管多個線程平安拜訪競爭資本,最簡略的同步機制是引入互斥鎖。互斥鎖為資本引入一個狀況:鎖定/非鎖定。某個線程要更改同享數據時,先將其鎖定,此時資本的狀況為“鎖定”,其他線程不克不及更改;直到該線程釋放資本,將資本的狀況釀成“非鎖定”,其他的線程能力再次鎖定該資本。互斥鎖包管了每次只要一個線程停止寫入操作,從而包管了多線程情形下數據的准確性。

threading模塊中界說了Lock類,可以便利的處置鎖定:

#創立鎖mutex = threading.Lock()
#鎖定mutex.acquire([timeout])
#釋放mutex.release()

個中,鎖定辦法acquire可以有一個超不時間的可選參數timeout。假如設定了timeout,則在超時後經由過程前往值可以斷定能否獲得了鎖,從而可以停止一些其他的處置。
應用互斥鎖完成下面的例子的代碼以下:

balance = 0
muxlock = threading.Lock()

def change_it(n):
  # 獲得鎖,確保只要一個線程操作這個數
  muxlock.acquire()
  global balance
  balance = balance + n
  balance = balance - n
  # 釋放鎖,給其他被壅塞的線程持續操作
  muxlock.release()

def run_thread(n):
  for i in range(10000):
    change_it(n)

加鎖後的成果,就可以確保數據准確:

[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved