Heim  >  Artikel  >  Java  >  Sperren Sie die Freigabe der Nutzungszusammenfassung

Sperren Sie die Freigabe der Nutzungszusammenfassung

巴扎黑
巴扎黑Original
2017-06-23 10:50:452099Durchsuche

Sperren

Im vorherigen Artikel haben wir darüber gesprochen, wie man das Schlüsselwort synchronisiert verwendet, um einen synchronen Zugriff zu erreichen. In diesem Artikel diskutieren wir dieses Problem weiter. Ab Java 5 wird eine weitere Möglichkeit zum Erreichen eines synchronen Zugriffs unter dem Paket java.util.concurrent.locks bereitgestellt, nämlich Lock.

Einige Freunde fragen sich vielleicht, warum wir eine Sperre bereitstellen müssen, da synchronisierter Zugriff durch Synchronisierung erreicht werden kann. Auf dieses Problem wird weiter unten eingegangen. Dieser Artikel beginnt mit den Mängeln der Synchronisierung, beschreibt dann die häufig verwendeten Klassen und Schnittstellen im Paket java.util.concurrent.locks und erörtert schließlich die folgenden Dinge zum Konzept von Sperren

synchronisierte Fehler

Wir haben zuvor gesagt, dass es zwei Situationen gibt, in denen ein synchronisierter Thread die Sperre aufhebt:

  1. Der Codeblock oder die synchronisierte Methode ist ausgeführt

  2. Wenn eine Ausnahme in einem Codeblock oder einer Synchronisationsmethode auftritt, gibt die JVM die Sperre automatisch frei

Sie können anhand der synchronisierten Freigabesperre oben erkennen, dass die Sperre erst freigegeben wird, wenn die Ausführung des synchronisierten Codeblocks abgeschlossen ist oder eine Ausnahme auftritt. Wenn das Programm im Codeblock aus E/A-Gründen blockiert ist, wird der Thread blockiert wird die Sperre nie aufheben, aber zu diesem Zeitpunkt führen andere Threads immer noch andere Programme aus. Dies wirkt sich stark auf die Ausführungseffizienz des Programms aus. Jetzt benötigen wir einen Mechanismus, der verhindern kann, dass der Thread auf unbestimmte Zeit wartet durch Sperre erfolgen

Darüber hinaus, wenn Es gibt ein Programm, das mehrere Lese-Threads und einen Schreib-Thread enthält Wir können wissen, dass synchronisiert nur von einem Thread ausgeführt werden kann, aber wir brauchen Mehrere Lesethreads können gleichzeitig gelesen werden, daher ist die Verwendung einer Synchronisierung definitiv nicht möglich, aber wir verwenden die Sperre auf die gleiche Weise. Dies ist möglich

Sperren

Wenn wir uns die API ansehen, können wir sehen, dass Lock eine Schnittstelle ist, sodass Objekte nicht direkt erstellt werden können. Wir können jedoch die von ihr implementierten Klassen zum Erstellen von Objekten verwenden. Machen Sie sich darüber keine Sorgen. Schauen wir uns zunächst an, welche Methoden implementiert sind durch die Lock-Klasse. Wir werden die spezifische Implementierung im Detail erläutern, wenn wir die von ihr implementierten Klassen

Methoden

  • lock() vorstellen , wenn nicht, wird es ewig warten

  • unlock() Sperre aufheben

  • tryLock() Versuchen Sie, die Sperre zu erhalten erfolgreich erworben, führen Sie sie aus. Wenn die Sperre nicht erfolgreich erworben wurde, gibt es keine Wartezeit.

  • lockInterruptibly() Wenn der aktuelle Thread nicht unterbrochen wird, wird die Sperre erworben.

ReentrantLock

ReentrantLock ist eine Klasse, die die Lock-Schnittstelle implementiert Dies bedeutet, dass es immer dem Thread zugewiesen wird, der kürzlich die Sperre erhalten hat. Um dieses Phänomen zu lösen, stellt die Konstruktionsmethode von ReentrantLock natürlich auch einen fairen Parameter bereit Fair ist wahr, es bedeutet, dass bei Verwendung des Fair-Zuteilungsmechanismus der Thread mit der längsten Wartezeit die Sperre erhält

Konstruktionsmethode

  • ReentrantLock() Erstellen Sie ein Objekt und verwenden Sie standardmäßig den Wiedereintrittsmechanismus

  • ReentrantLock(boolean fair) Wenn fair wahr ist, wird der faire Zuordnungsmechanismus verwendet

Gemeinsame Methoden

  • lock() Erwerben Sie die Sperre. Wenn sie nicht erworben wird, wird sie immer blockiert

Im Folgenden wird ein Programm verwendet, um die Verwendung der folgenden Sperrmethode zu demonstrieren. Der Code lautet wie folgt:

//实现接口的线程类public class MyThread implements Runnable {public ReentrantLock rLock = null;  //注意这里的锁一定要是全局变量,否则每一个线程都创建一把锁,那么将会毫无意义
 public MyThread() {this.rLock = new ReentrantLock(); // 创建默认的可重入锁}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {this.rLock.lock(); // 获取锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出"+ i);
            }
        } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,new Thread(thread).start();new Thread(thread).start();

    }
}

Führen Sie aus Geben Sie den obigen Code ein, um das unten gezeigte Ergebnis zu erhalten:

Sperren Sie die Freigabe der Nutzungszusammenfassung

Aus den obigen Ergebnissen können wir ersehen, dass die Threads eins ausgeben um einen, und der nächste kann erst ausgeführt werden, nachdem auf den Abschluss der Ausgabe eines Threads gewartet wurde. Ein Thread gilt hier nur für den Code zwischen Sperren und Entsperren, der andere Code wird nicht gesteuert

Hinweis: Die Erstellung hier kann wiederholt werden. Das gesperrte Objekt muss eine globale Variable für jeden Thread und ein Objekt sein, das gemeinsam genutzt werden kann. Wenn Sie dieses Objekt in der Anzeigemethode erstellen, ist es bedeutungslos Nicht jeder Thread verwendet die gleiche Sperre. Versuchen Sie zunächst, die Sperre zu erhalten, andernfalls führen Sie sie aus es wird nicht ewig warten

  • Verwenden Sie einen Code, um die folgende Methode auszuprobieren. Der Code lautet wie folgt:boolean tryLock()

执行后的Sperren Sie die Freigabe der Nutzungszusammenfassung如下图:

Sperren Sie die Freigabe der Nutzungszusammenfassung

从上面的Sperren Sie die Freigabe der Nutzungszusammenfassung我们知道线程0获取了锁开始执行,但是线程1并没有获取锁,但是使用的是tryLock并不是lock,因此不会一直等待下去,所以直接程序向下运行,直接跳过上锁的代码段,因此就输出了上面的那句话后直接结

ReadWriteLock

从API中可以知道,这个也是一个接口,用于实现读写线程,他有两个方法:Lock readLock(),Lock writeLock() 分别用于获得读锁和写锁,指定特定的锁可以实现特定的功能,比如读锁可以在写线程在执行的情况下可以实现多个读线程进行操作,下面我们来介绍它的具体的实现的类ReentrantReadWriteLock

ReentrantReadWriteLock

这个类也是一个可重入分配的类,当然前面已经说过了什么是可重入,现在我们来说说说这个类的详细的用法

构造方法

  • ReentrantReadWriteLock() 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。

  • ReentrantReadWriteLock(boolean fair) 使用给定的公平策略创建一个新的ReentrantReadWriteLock。

常用的方法

  • ReentrantReadWriteLock.ReadLock readLock() 用于返回读取操作的锁

前面已经说过读取操作的锁是用来实现多个线程共同执行的,代码如下:

//实现接口的线程类public class MyThread implements Runnable {public ReentrantReadWriteLock rwlock = null;public Lock rLock = null;public MyThread() {this.rwlock = new ReentrantReadWriteLock(); // 创建对象,使用的是非公平的this.rLock = this.rwlock.readLock(); // 获取读取锁对象}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {this.rLock.lock(); // 获取读取锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出"+ i);
            }
        } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,for(int i=0;i<5;i++)
        {new Thread(thread).start();
        }
        


    }
}

执行上面的程序Sperren Sie die Freigabe der Nutzungszusammenfassung如下:

Sperren Sie die Freigabe der Nutzungszusammenfassung

从上面的Sperren Sie die Freigabe der Nutzungszusammenfassung可以知道,其实使用读取操作是多个线程同时进行读取的操作,因此一定要小心谨慎的使用,根据自己的需求,一般不能在里面进行修改了,因为出现Sperren Sie die Freigabe der Nutzungszusammenfassung不准确的Sperren Sie die Freigabe der Nutzungszusammenfassung,这个就不多说了,相信大家都明白,总之要小心使用

  • ReentrantReadWriteLock.WriteLock writeLock() 返回用于写入操作的锁

写入操作的锁和读取操作的锁不一样了,因为一次只能允许一个线程执行写入操作。

并且如果一个线程已经占用了读锁,另外一个线程申请写锁将会一直等待线程释放读锁。

如果一个线程已经占用了写锁,另外一个线程申请读锁,那么这个线程将会一直等待线程释放写锁才能执行。

总之意思就是写线程和读线程不能同时执行,但是多个读线程可以同时执行

下面将使用一个程序详细的体会以下读写锁的综合使用,代码如下:

//实现接口的线程类public class MyThread {public ReentrantReadWriteLock rwlock = null;public Lock rLock = null;public Lock wLock = null;public ArrayList<Integer> arrayList = null;public MyThread() {this.rwlock = new ReentrantReadWriteLock(); // 创建对象,使用的是非公平的this.rLock = this.rwlock.readLock(); // 获取读取锁对象arrayList = new ArrayList<>(); // 实例化this.wLock = this.rwlock.writeLock(); // 获取写入锁对象}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞// //向arraylist中写入数据public void put() {this.wLock.lock(); // 获取写入锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()
                        + "正在执行写入操作,写入" + i);this.arrayList.add(i);
            }
        } finally {this.wLock.unlock();
        }

    }// 从arraylist中读取数据,这里只是随机读取使用的是get,并没有做什么修改,因为这仅仅是读取操作,如果进行了修改必须实现同步public void get() {this.rLock.lock(); // 获取读取操作的锁Random random = new Random();if (!arrayList.isEmpty()) {try {for (int i = 0; i < 10; i++) {int index = random.nextInt(this.arrayList.size() - 1);int data = this.arrayList.get(index);
                    System.out.println(Thread.currentThread().getName()
                            + "正在读取数据     " + data);
                }
            } finally {this.rLock.unlock();

            }
        } else {
            System.out.println("ArrayList为空");
        }

    }

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象ArrayList<Thread> arrayList = new ArrayList<>();/*         * 创建8个读线程,2个写线程         */for (int i = 0; i < 2; i++) {
            arrayList.add(new Thread() {@Overridepublic void run() {
                    thread.put();
                }
            });

        }        for(int i=0;i<8;i++)
        {
            arrayList.add(new Thread(){@Overridepublic void run() {
                    thread.get();
                }
            });
        }        
        
        
        for (Thread t : arrayList) {
            t.start();
        }

    }
}

Sperren Sie die Freigabe der Nutzungszusammenfassung如下图:

Sperren Sie die Freigabe der Nutzungszusammenfassung

从上面可以看出写入线程都是一个一个执行的,读取线程是一起执行的

注意: 所有的锁对象对于线程来说必须是全局变量,否则毫无意义。读线程只能进行不影响线程安全性的操作,比如不能进行对数据的修改插入,如果想要进行修改的话必须还要使用锁对必要的代码实现同步操作

参考文章


Das obige ist der detaillierte Inhalt vonSperren Sie die Freigabe der Nutzungszusammenfassung. 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