Wenn mehrere Threads auf eine Klasse zugreifen, unabhängig davon, welche Aufrufmethode die Laufzeitumgebung verwendet oder wie diese Threads abwechselnd ausgeführt werden, ist im Hauptaufrufcode nichts erforderlich Diese Klasse kann trotz zusätzlicher Synchronisierung oder Koordination korrektes Verhalten zeigen, dann wird diese Klasse als Thread-sicher bezeichnet.
Zustandslose Objekte müssen Thread-sicher sein, wie zum Beispiel: Servlet
. Servlet
。
由于不恰当的执行时序而出现不正确的结果的情况,就是竞争条件。
“先检查后执行”操作,即通过一个可能实效的观测结果来决定下一步的动作。比如:延迟初始化。
if(instance == null) { instance = new SomeObject(); }
“读取-修改-写入”的操作,其结果状态依赖于之前的状态。如:递增运算。
long count = 0; count++;
原子操作是指,对于访问同一个状态的所有操作(包括此操作本身)来说,这个操作是以一个原子方式执行(不可分割)的操作。
为了确保线程安全性,包含了一组必须以原子方式执行的操作,称为复合操作。
递增运算可以使用一个现有的线程安全类,确保线程安全性。如:
AtomicLong count = new AtomicLong(0); count.incrementAndGet();
如果一个类只有一个状态变量,可以通过使用线程安全的状态变量来确保类的线程安全性。当一个类有更多的状态时,仅仅添加更多的线程安全状态变量是不够的。为了确保状态的一致性,必须在单个原子操作中更新所有相关的状态变量。
Java提供一种内置锁:同步代码块,它包括:一个作为锁的对象引用、一个作为由这个锁保护的代码块。
以关键字synchronized
来修饰的方法就是一种横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象。静态的synchronized
public class Widget { public synchronized void doSomething() { //...... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { //...... super.doSomething();//假如没有可重入的锁,该语句将产生死锁。 } }„Read-Modify-Write“-Operation, der Ergebniszustand hängt vom vorherigen Zustand ab. Zum Beispiel: Inkrementierungsvorgang.
@ThreadSafe public class SynchronizedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public synchronized void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); if (i.equals(lastNumber)) encodeIntoResponse(resp, lastFactors); else { BigInteger[] factors = factor(i);//因数分解计算 lastNumber = i; lastFactors = factors;//存放上一次计算结果 encodeIntoResponse(resp, factors); } } }
public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = null; synchronized (this) { if (i.equals(lastNumber)) { factors = lastFactors.clone(); } } if (factors == null) { factors = factor(i); synchronized (this) { lastNumber = i; lastFactors = factors.clone(); } } encodeIntoResponse(resp, factors); } }3. Sperrmechanismus🎜🎜Wenn eine Klasse nur eine Statusvariable hat, können Sie die Thread-Sicherheit der Klasse sicherstellen, indem Sie threadsichere Statusvariablen verwenden. Wenn eine Klasse über mehr Status verfügt, reicht es nicht aus, einfach weitere threadsichere Statusvariablen hinzuzufügen. Um die Zustandskonsistenz sicherzustellen, müssen alle relevanten Zustandsvariablen in einer einzigen atomaren Operation aktualisiert werden. 🎜🎜3.1 Integrierte Sperre🎜🎜Java bietet eine integrierte Sperre: Synchronisierter Codeblock, er umfasst: Eine Objektreferenz als Sperre und einen Code als geschützt durch dieses Schlossstück. 🎜🎜Die mit dem Schlüsselwort
synchronized
geänderte Methode ist ein synchronisierter Codeblock, der den gesamten Methodenkörper umfasst, und die Sperre des synchronisierten Codeblocks ist das Objekt, in dem die Methode aufgerufen wird. Die statische synchronized
-Methode verwendet das Class-Objekt als Sperre. 🎜🎜Wenn ein Thread einen synchronisierten Codeblock betritt, erhält er automatisch die Sperre, und wenn der Thread den synchronisierten Codeblock verlässt, wird die Sperre automatisch freigegeben. Maximal ein Thread kann diese Sperre halten, sodass synchronisierter Code atomar ausgeführt wird. 🎜🎜3.2 Wiedereintrittsfähigkeit🎜🎜Die integrierte Sperre ist wiedereintrittsfähig, was bedeutet, dass die Granularität der Operation zum Erlangen der Sperre der Thread und nicht der Aufruf ist. Wenn ein Thread versucht, eine bereits von ihm gehaltene Sperre erneut zu erhalten, ist die Anforderung ebenfalls erfolgreich. 🎜🎜Reentrancy verbessert die Kapselung des Sperrverhaltens weiter und vereinfacht die Entwicklung von objektorientiertem gleichzeitigem Code. 🎜rrreee🎜4. Schützen Sie den Status mit einer Sperre🎜🎜Für eine variable Statusvariable, auf die mehrere Threads gleichzeitig zugreifen können, müssen Sie beim Zugriff darauf die gleiche Sperre beibehalten durch diese Sperre geschützt gesteuert werden. 🎜🎜5. Lebendigkeit und Leistung🎜🎜Die grobkörnige Verwendung von Sperren gewährleistet die Thread-Sicherheit, kann jedoch zu Leistungsproblemen und Lebendigkeitsproblemen führen, wie zum Beispiel: 🎜rrreee🎜Sie können sicherstellen, dass das Servlet konsistent ist, indem Sie die Blockfrisur des Synchronisationscodes verkleinern , unter Beibehaltung der Thread-Sicherheit. Teilen Sie nicht atomare Vorgänge in mehrere synchronisierte Codeblöcke auf. Versuchen Sie, Vorgänge, die keinen Einfluss auf den gemeinsamen Status haben und deren Ausführung lange dauert, vom synchronisierten Code zu trennen. Wie zum Beispiel: 🎜rrreeeDas obige ist der detaillierte Inhalt vonSo implementieren Sie Thread-Sicherheit in der gleichzeitigen Java-Programmierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!