Heim  >  Artikel  >  Java  >  Was ist Java-Thread-Synchronisation?

Was ist Java-Thread-Synchronisation?

爱喝马黛茶的安东尼
爱喝马黛茶的安东尼Original
2019-12-09 16:27:533351Durchsuche

Was ist Java-Thread-Synchronisation?

Thread-Synchronisierung

Wenn Sie dasselbe Objekt zwischen mehreren Threads aufrufen, muss das Objekt aus Sicherheits- und Genauigkeitsgründen synchronisiert werden, um sicherzustellen, dass das Ergebnis des Objekts bei Verwendung durch jeden Thread korrekt ist und der Status des Objekts angemessen ist Dieser Teil umfasst Wissenspunkte wie Synchronisation und Thread-Sperre. In diesem Teil geht es nur um die Konzepte Synchronisation und Synchronisationssperre (Sperre).

synchronized

Das synchronisierte Schlüsselwort kann Objekte und Methoden ändern. Die übliche Verwendung ist wie folgt:

//同步代码块
synchronized(Object object){
...
}
//或者
//同步方法
public synchronized void test(){
...
}

Es gibt ein Konzept des Synchronisationsmonitors. wie oben: Das Objektobjekt des synchronisierten Codeblocks und dieses Objekt der synchronisierten Methode werden synchron überwacht. Wenn mehrere Threads gleichzeitig einen synchronisierten Codeblock oder eine synchronisierte Methode aufrufen, kann nur ein Thread die synchron überwachte Objektsperre erhalten Zu jedem Zeitpunkt nach der Ausführung des Codes Die Sperre wird später aufgehoben. Während dieser Zeit können andere aufrufende Threads nur warten, bis die Sperre aufgehoben wird.

Die oben erwähnte Verkaufsmethode in der SellRunnable-Klasse verwendet auch synchronisiert. Der obige Code wird zu schnell ausgeführt, sodass er nicht erkannt werden kann. Wenn Sie ihn ändern, können Sie den Unterschied verstehen, ob synchronisiert ist oder nicht.

public class ThreadTest {
    public static void main(String[] args) {
        SellRunnable sellRunnable = new SellRunnable();
        Thread thread1 = new Thread(sellRunnable, "1");
        Thread thread2 = new Thread(sellRunnable, "2");
        Thread thread3 = new Thread(sellRunnable, "3");
        thread2.start();
        thread1.start();
        thread3.start();
    }
}
class SellRunnable implements Runnable {
    //有十张票
    int index = 10;
    public void sell() {
        if (index >= 1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            index--;
            System.out.println("售货窗口:" + Thread.currentThread().getName() + 
            " 卖出了一张票,剩余:
            " + index);
        } else {
            System.out.println("售货窗口:" + Thread.currentThread().getName() + " 买票时没票了");
        }
    }
    @Override
    public void run() {
        while (index > 0) {
            System.out.println("售货窗口:" + Thread.currentThread().getName() + " 开始买票");
            sell();
        }
    }
}
//执行结果:
售货窗口:1 开始买票
售货窗口:2 开始买票
售货窗口:3 开始买票
售货窗口:2  卖出了一张票,剩余:9
售货窗口:2 开始买票
售货窗口:1  卖出了一张票,剩余:9
售货窗口:1 开始买票
售货窗口:3  卖出了一张票,剩余:8
售货窗口:3 开始买票
售货窗口:1  卖出了一张票,剩余:6
售货窗口:1 开始买票
售货窗口:2  卖出了一张票,剩余:6
售货窗口:2 开始买票
售货窗口:3  卖出了一张票,剩余:5
售货窗口:3 开始买票
售货窗口:1  卖出了一张票,剩余:4
售货窗口:1 开始买票
售货窗口:2  卖出了一张票,剩余:3
售货窗口:3  卖出了一张票,剩余:2
售货窗口:3 开始买票
售货窗口:2 开始买票
售货窗口:3  卖出了一张票,剩余:1
售货窗口:2  卖出了一张票,剩余:0
售货窗口:1  卖出了一张票,剩余:1
Process finished with exit code 0  //可以看到,票数减少是错误的
//sell方法添加synchronized修饰符后 执行结果:
public synchronized void sell() {
        if (index >= 1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            index--;
            System.out.println("售货窗口:" + Thread.currentThread().getName() + 
            " 卖出了一张票,剩余:
            " + index);
        } else {
            System.out.println("售货窗口:" + Thread.currentThread().getName() + " 买票时没票了");
        }
    }
售货窗口:2 开始买票
售货窗口:3 开始买票
售货窗口:1 开始买票
售货窗口:2  卖出了一张票,剩余:9
售货窗口:2 开始买票
售货窗口:1  卖出了一张票,剩余:8
售货窗口:1 开始买票
售货窗口:3  卖出了一张票,剩余:7
售货窗口:3 开始买票
售货窗口:1  卖出了一张票,剩余:6
售货窗口:1 开始买票
售货窗口:2  卖出了一张票,剩余:5
售货窗口:2 开始买票
售货窗口:1  卖出了一张票,剩余:4
售货窗口:1 开始买票
售货窗口:1  卖出了一张票,剩余:3
售货窗口:1 开始买票
售货窗口:3  卖出了一张票,剩余:2
售货窗口:3 开始买票
售货窗口:1  卖出了一张票,剩余:1
售货窗口:1 开始买票
售货窗口:1  卖出了一张票,剩余:0
售货窗口:2 买票时没票了
售货窗口:3 买票时没票了
Process finished with exit code 0  // 可以看到,票数是正常减少的

Nach der obigen Synchronisierung der Verkaufsmethode ruft zu einem bestimmten Zeitpunkt nur ein Thread diese Methode auf, sodass das bei der Indexbeurteilung erhaltene Ergebnis das richtige Ergebnis ist.

Während der oben genannten Synchronisierung wird die Thread-Sicherheit durch Reduzierung der Betriebseffizienz gewährleistet. Synchronisieren Sie daher keine unnötigen Methoden und Objekte in Thread-Nutzungsklassen und synchronisieren Sie nur Ressourcen oder Objekte mit dem Code.

Nach der Synchronisationsidentifikation können die folgenden Punkte die Sperre aufheben:

Codeblock, Methodenausführung abgeschlossen (normaler Abschluss, Rückgabe oder Unterbrechung, ausgelöste Ausnahme)

Aufruf Die Wartezeit -Methode wird verwendet, um den aktuellen Thread anzuhalten.

Wenn der Thread einen synchronisierten Codeblock ausführt, geben die Sleep- und Yield-Methoden weder die Synchronisationssperre auf noch wird die Suspend-Methode suspendiert (versuchen Sie zu vermeiden, den Thread-Status während des Thread-Betriebs mit Suspend und Resume zu manipulieren). was leicht zu einem Deadlock führt. )

Synchronisierte Sperre

Die oben erwähnte Synchronisierung ist ein Schlüsselwort in Java und wird auch in erwähnt Beim Schlafen oder Ausführen von E/A-Vorgängen gibt der Thread die Thread-Sperre nicht frei, und andere Threads müssen warten. Dies verringert manchmal die Ausführungseffizienz, sodass eine Alternative erforderlich ist, die die Thread-Sperre aufheben kann, wenn der Thread blockiert ist nur um dieses Problem zu lösen.

Lock ist eine Klasse in Java. Im Paket java.util.concurrent.locks lautet der spezifische Code wie folgt:

public interface Lock {
    void lock();//加锁
    void lockInterruptibly() throws InterruptedException;//加锁
    boolean tryLock();//加锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//加锁
    void unlock();//释放锁
    Condition newCondition();//线程协作中用到
}

Eine Implementierungsunterklasse der Lock-Schnittstelle ist ReentrantLock in Java . Unter dem Paket util.concurrent.locks lautet der Quellcode von ReentrantLock wie folgt:

public class ReentrantLock implements Lock, Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    private final ReentrantLock.Sync sync;
    public ReentrantLock() {
        this.sync = new ReentrantLock.NonfairSync();
    }
    public ReentrantLock(boolean var1) {//是否创建公平锁
        this.sync = (ReentrantLock.Sync)(var1?new ReentrantLock.FairSync():new  ReentrantLock.
        NonfairSync());
    }
    public void lock() {
        this.sync.lock();
    }
    public void lockInterruptibly() throws InterruptedException {
        this.sync.acquireInterruptibly(1);
    }
    public boolean tryLock() {
        return this.sync.nonfairTryAcquire(1);
    }
    public boolean tryLock(long var1, TimeUnit var3) throws InterruptedException {
        return this.sync.tryAcquireNanos(1, var3.toNanos(var1));
    }
    public void unlock() {
        this.sync.release(1);
    }
    public Condition newCondition() {
        return this.sync.newCondition();
    }
    public int getHoldCount() {//当前线程持有该锁的数量
        return this.sync.getHoldCount();
    }
    public boolean isHeldByCurrentThread() {//该锁是否被当前线程持有
        return this.sync.isHeldExclusively();
    }
    public boolean isLocked() {//是否被其他线程持有该锁
        return this.sync.isLocked();
    }
    public final boolean isFair() {//是否是公平锁
        return this.sync instanceof ReentrantLock.FairSync;
    }
    protected Thread getOwner() {//当前锁的持有线程
        return this.sync.getOwner();
    }
    public final boolean hasQueuedThreads() {//是否有线程在等待该锁
        return this.sync.hasQueuedThreads();
    }
    public final boolean hasQueuedThread(Thread var1) {//目标线程是否在等待该锁
        return this.sync.isQueued(var1);
    }
    public final int getQueueLength() {//等待该锁线程的数量
        return this.sync.getQueueLength();
    }
    protected Collection<Thread> getQueuedThreads() {//获取所有等待该锁的线程集合
        return this.sync.getQueuedThreads();
    }
    ...
    
}

So verwenden Sie Lock

lock

lock() wird verwendet, um die Sperre zu erhalten. Wenn die Sperre von anderen Threads belegt ist, wird sie warten.

public class LockTest {
    public static void main(String[] args) {
        com.test.java.SellRunnable sellRunnable = new com.test.java.SellRunnable();
        Thread thread1 = new Thread(sellRunnable, "1号窗口");
        Thread thread2 = new Thread(sellRunnable, "2号窗口");
        Thread thread3 = new Thread(sellRunnable, "3号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
public class SellRunnable implements Runnable {
    //有十张票
    int index = 10;
    Lock lock = new ReentrantLock();
    public void sell() {
        try {
            lock.lock();
            System.out.println("售货柜台:" + Thread.currentThread().getName() + 
            "获取了票源+++++");
            if (index >= 1) {
                index--;
                System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                "卖出了一张票,剩余:
                " + index);
            } else {
                System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                "买票时没票了000");
            }
        } finally {
            lock.unlock();
        }
    }
    @Override
    public void run() {
        while (index > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();
        }
    }
}

Laufergebnis:

售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:9
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:8
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:7
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:6
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:5
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:4
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:3
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:2
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:1
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:0
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口买票时没票了000
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口买票时没票了000
Process finished with exit code 0  //每一个窗口都随机获取票源、然后卖出票

tryLock

tryLock() versucht, die Sperre zu erhalten, wenn sie fehlschlägt, gibt sie false zurück und wird nicht in den Wartezustand wechseln.

public class SellRunnable implements Runnable {
    //有十张票
    int index = 10;
    Lock lock = new ReentrantLock();
    public void sell() {
        if (lock.tryLock()) {
            try {
                System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                "获取了票源+++++");
                if (index >= 1) {
                    index--;
                    System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                    "卖出了一张票,剩余:" + index);
                } else {
                    System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                    "买票时没票了000");
                }
            } finally {
                lock.unlock();
            }
        } else {
           System.out.println("售货柜台:" + Thread.currentThread().getName()+"没有获取票源!!!");
        }
    }
    @Override
    public void run() {
        while (index > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();
        }
    }
}

Laufende Ergebnisse:

售货柜台:1号窗口获取了票源+++++
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口没有获取票源!!!
售货柜台:1号窗口卖出了一张票,剩余:9
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:8
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:7
售货柜台:1号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:6
售货柜台:1号窗口获取了票源+++++
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口卖出了一张票,剩余:5
售货柜台:2号窗口获取了票源+++++
售货柜台:1号窗口没有获取票源!!!
售货柜台:2号窗口卖出了一张票,剩余:4
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口获取了票源+++++
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口卖出了一张票,剩余:3
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:2
售货柜台:2号窗口获取了票源+++++
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口卖出了一张票,剩余:1
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:0
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口没有获取票源!!!
Process finished with exit code 0//没有获取到货源的票口,就直接没有等待,进入下次买票

tryLock(long time, TimeUnit-Einheit)

tryLock(long time, TimeUnit-Einheit) kann so eingestellt werden, dass eine bestimmte Zeitspanne gewartet wird Die Sperre kann nicht erhalten werden. //Der erste Parameter ist lang, die Zeiteinheit des zweiten Parameters ist

public class SellRunnable implements Runnable {
    //有十张票
    int index = 10;
    Lock lock = new ReentrantLock();
    public void sell() {
        try {
            if (lock.tryLock(1000, TimeUnit.MILLISECONDS)) {
                try {
                    System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                    "获取了票源+++++");
                    if (index >= 1) {
                        index--;
                        System.out.println("售货柜台:" + Thread.currentThread().getName()
                         +"卖出了一张票,剩余:" + index);
                    } else {
                        System.out.println("售货柜台:" + Thread.currentThread().
                        getName()  + "买票时没票了000");
                    }
                    try {
                        Thread.sleep(2000);//人为加入买票时间
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("售货柜台:" + Thread.currentThread().getName() + 
                "没有获取票源!!!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while (index > 0) {
            try {
                Thread.sleep(500);//要不执行太快,看不出效果
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();
        }
    }
}

Ausführungsergebnis:

售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:9
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:8
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口没有获取票源!!!
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:7
售货柜台:1号窗口没有获取票源!!!
售货柜台:2号窗口没有获取票源!!!
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:6
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:5
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口没有获取票源!!!
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:4
售货柜台:1号窗口没有获取票源!!!
售货柜台:2号窗口没有获取票源!!!
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:3
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:2
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口没有获取票源!!!
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:1
售货柜台:1号窗口没有获取票源!!!
售货柜台:2号窗口没有获取票源!!!
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:0
售货柜台:2号窗口没有获取票源!!!
售货柜台:3号窗口没有获取票源!!!
Process finished with exit code 0 //当买票时间大约等待时间时,则没有获取票源的窗口不买票,进入下个买票机会

Ticketkaufzeit verkürzen:

try {
    Thread.sleep(500);//人为加入买票时间
} catch (InterruptedException e) {
    e.printStackTrace();
}

Ausführungsergebnis:

售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:9
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:8
售货柜台:3号窗口没有获取票源!!!
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:7
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:6
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:5
售货柜台:3号窗口没有获取票源!!!
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:4
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:3
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:2
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:1
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:0
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口买票时没票了000
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口买票时没票了000
Process finished with exit code 0 //等待时间内获取到票源了,也就卖出票了

lockInterruptably

lockInterruptably() Wenn Sie eine Sperre über diese Methode erhalten und die Sperre von einem anderen Thread gehalten wird, wechselt sie in einen Wartezustand. Dieser Wartevorgang kann jedoch durch Aufrufen von Thread The unterbrochen werden Die Interrupt-Methode des Objekts kann das Warten unterbrechen. Bei einer Unterbrechung wird eine InterruptedException ausgelöst, die abgefangen oder zum Auslösen deklariert werden muss.

public class ThreadTest {
    public static void main(String[] args) {
        SellRunnable sellRunnable = new SellRunnable();
        Thread thread1 = new Thread(sellRunnable, "1号窗口");
        Thread thread2 = new Thread(sellRunnable, "2号窗口");
        Thread thread3 = new Thread(sellRunnable, "3号窗口");
        thread1.start();
        try {
            Thread.sleep(500);//确保窗口1号先获取锁
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
        thread3.start();
        try {
            Thread.sleep(2000);//等待两秒后,打断窗口2、3的等待
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.interrupt();
        thread3.interrupt();
    }
}
SellRunnable中等待时间加长:
try {
    Thread.sleep(5000);//人为加入买票时间
} catch (InterruptedException e) {
    e.printStackTrace();
}

Ausführungsergebnisse:

售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:9
售货柜台:3号窗口被打断了      //这个地方被打断了
售货柜台:2号窗口被打断了      //这个地方被打断了
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:8
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:7
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:6
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:5
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:4
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:3
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口卖出了一张票,剩余:2
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口卖出了一张票,剩余:1
售货柜台:1号窗口获取了票源+++++
售货柜台:1号窗口卖出了一张票,剩余:0
售货柜台:2号窗口获取了票源+++++
售货柜台:2号窗口买票时没票了000
售货柜台:3号窗口获取了票源+++++
售货柜台:3号窗口买票时没票了000
Process finished with exit code 0

Vergleich zwischen synchronisiert und gesperrt

Durch den obigen Code können Sie den Unterschied erkennen Zwischen Lock und Synchronized gibt es mehrere Verbindungen und Unterschiede:

Beide sind Wiedereintrittssperren

Wiedereintrittssperre bedeutet, dass der Thread die Objektsperre erneut erwerben kann, nachdem er sie erhalten hat. Nachdem beispielsweise mehrere Methoden (oder eine rekursiv aufgerufene Methode) in derselben Klasse synchronisiert oder gesperrt wurden, kann derselbe Thread die Sperre des Objekts erhalten, ohne beim Aufrufen dieser beiden Methoden blockiert zu werden.

Beispiel für eine nicht wiedereintrittsfähige Sperre:

public class Lock{
    private boolean isLocked = false;
    public void lock(){
        while(isLocked){    
            wait();
        }
        isLocked = true;
    }
    public void unlock(){
        isLocked = false;
        notify();
    }
}
//使用方法:
public class Test{
    Lock lock = new Lock();
    public void test1(){
        lock.lock();
        test2();
        lock.unlock();
    }
    public void test2(){
        lock.lock();
        ...
        lock.unlock();
    }
}

Wenn die Testklasse nach der Ausführung von lock.lock() und dem Aufruf von test2 die Methode test1 aufruft, wartet sie ewig und wird zur Sperre.

Reentrant Lock Design-Prinzip:

public class Lock{
    private boolean isLocked = false;
    private Thread lockedThread = null;
    int lockedCount = 0;
    public void lock(){
        Thread thread = Thread.currentThread();
        while(isLocked && thread != lockedThread){    
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedThread = thread;
    }
    public void unlock(){
        Thread thread = Thread.currentThread();
        if(thread == lockedThread){    
            lockedCount--;
            if(lockedCount == 0){
                isLocked = false;
                lockedThread = null;
                notify();
            }
        }
    }
}

Nachdem die test1-Methode der Testklasse auf diese Weise aufgerufen wurde, kann auch die test2-Methode reibungslos ausgeführt werden.

Die Implementierung von synchronisiert verwendet grundsätzlich Zähler, um einen Wiedereintritt zu erreichen.

Sperre ist eine unterbrechbare Sperre, synchronisiert kann nicht unterbrochen werden.

当一个线程B执行被锁的对象的代码时,发现线程A已经持有该锁,那么线程B就会进入等待,但是synchronized就无法中断该等待过程,而Lock就可以通过lockInterruptibly方法抛出异常从而中断等待,去处理别的事情。

Lock可创建公平锁,synchronized是非公平锁。

公平锁的意思是按照请求的顺序来获取锁,不平公锁就无法保证线程获取锁的先后次序。

Lock可以知道是否获取到锁,synchronized不可以。

synchronized在发生异常或者运行完毕,会自动释放线程占有的锁。而Lock需要主动释放锁,否则会锁死;

synchronized在阻塞时,别的线程无法获取锁,Lock可以(这也是lock设计的一个目的)。

读写锁

多个线程对同一个文件进行写操作时,会发生冲突所以需要加锁,但是对同一个文件进行读操作的时候,使用上面的方法会造成效率的降低,所以基于这种情况,产生了ReadWriteLock这个接口:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();//读的锁
 
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();//写的锁
}

这个接口的实现类是ReentrantReadWriteLock,其源代码如下:

public class ReentrantReadWriteLock implements ReadWriteLock, Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    private final ReentrantReadWriteLock.ReadLock readerLock;
    private final ReentrantReadWriteLock.WriteLock writerLock;
    ...
    public ReentrantReadWriteLock.WriteLock writeLock() {//获取write lock
        return this.writerLock;
    }
    public ReentrantReadWriteLock.ReadLock readLock() {//获取read lock
        return this.readerLock;
    }
    ...
}

使用方法和Lock一样,使用到write时调用writeLock()方法获取lock进行加锁,使用到read时调用readLock()方法进行加锁,需要注意的知识点如下:

线程A占用写锁,线程B在申请写、读的时候需要等待。

线程A占用读锁,线程B在申请写操作时,需要等待。

线程A占用读锁,线程B获取读操作时可以获取到。

总结

如果需要效率提升,则建议使用Lock,如果效率要求不高,则synchronized满足使用条件,业务逻辑写起来也简单,不需要手动释放锁。

PHP中文网,有大量免费的JAVA入门教程,欢迎大家学习!

Das obige ist der detaillierte Inhalt vonWas ist Java-Thread-Synchronisation?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn