Semaphore es una clase comúnmente utilizada en el paquete JUC. Es una aplicación del modo de intercambio AQS. Puede permitir que varios subprocesos funcionen con recursos compartidos al mismo tiempo y puede controlar efectivamente el número de concurrencia. Puede lograr un buen control de tráfico. Semaphore proporciona un concepto de una licencia, que puede considerarse como un boleto de autobús. Solo aquellos que obtienen con éxito el boleto pueden subir al autobús. Hay un cierto número de boletos y es imposible emitirlos sin restricciones, lo que conducirá a la sobrecarga del autobús. Entonces, cuando se emite el boleto (el autobús está lleno), los otros solo pueden esperar el próximo tren. Si alguien se baja del autobús a mitad de camino, su posición será gratuita, por lo que si otros quieren subir al autobús en este momento, pueden volver a obtener boletos. Se pueden implementar varios grupos utilizando Semaphore. Al final de este artículo, escribiremos un grupo de conexión de base de datos simple. Primero, echemos un vistazo al constructor de Semaphore.
// Constructor 1Public Semaphore (int Permits) {sync = new Nonfairsync (permisos);} // constructor 2public semafore (int permiss, boolean fair) {sync = jair? NUEVO FAIRSYNC (PERMIS): Nueva nonfairsync (permisos);}Semaphore proporciona dos constructores sin parámetros, pero no se proporcionan constructores sin parámetros. Ambos constructores deben aprobar un número inicial de licencias. El semáforo construido con el constructor 1 se obtendrá de forma no fair al obtener la licencia. El uso del Constructor 2 puede especificar el método para obtener la licencia a través de los parámetros (justo o injusto). Semaphore proporciona principalmente dos tipos de API al mundo exterior, la obtención de licencias y la liberación de licencias. El valor predeterminado es obtener y liberar una licencia, y los parámetros también se pueden aprobar para obtener y liberar múltiples licencias al mismo tiempo. En este artículo, solo hablaremos sobre la situación de obtener y liberar una licencia cada vez.
1. Obtenga una licencia
// Obtener una licencia (respuesta de respuesta) public void adquirir () lanza interruptedException {sync.AcquiresharedInterruption (1);} // Obtener una licencia (no responder a la interrupción) public void adquirinterrumty () {sync.acquireshared (1);} // Intenta obtener una licencia (no fair) public boolean tryacquire () Sync.nonfairtryAcquireshared (1)> = 0;} // Intenta obtener una licencia (tiempo de espera largo, unidad de tiempo de tiempo) arroja interruptedException {return sync.tryacquireshareNANOS (1, unit.tonanos (timeout));}La API anterior es la operación de adquisición de licencia predeterminada proporcionada por Semaphore. Solo obtener una licencia a la vez también es una situación común en la vida real. Además de la obtención directa, también proporciona un intento de obtener. La operación de obtención directa puede bloquear el hilo después de la falla, mientras que intentar obtener no lo hará. También se debe tener en cuenta que el método Tryacquire se usa para tratar de obtenerlo de manera injusta. Lo que a menudo usamos en tiempos normales es obtener una licencia. Echemos un vistazo a cómo se obtiene. Puede ver que el método adquirir directamente llama a syncquiresharedInterruptiblemente (1). Este método es el método en AQS. Una vez hablamos sobre los artículos de la serie de código fuente AQS. Vamos a revisarlo de nuevo.
// Adquirir el bloqueo en modo interrumpible (modo compartido) público Final void adquirido de interruptuamente (int arg) lanza interruptedException {// Determinar primero si el hilo está interrumpido, de ser así, lanza una excepción if (thread.interrupted) {tirar nueva interruptexception (); } // 1. Intente adquirir el bloqueo if (tryAcquireshared (arg) <0) {// 2. Si la adquisición falla, ingrese el método doAcquiresharedInterruptiblemente (arg); }}El primer método adquirido de manera intensible es llamar al método Tryacquireshared para intentar obtener. Tryacquireshared es un método abstracto en AQS. Las dos clases derivadas Fairsync y NonfairSync implementan la lógica de este método. Fairsync implementa la lógica de adquisición justa, mientras que NonfairSync implementa la lógica de la adquisición no fair.
La sincronización de la clase estática abstracta extiende AbstractQueueynChronizer {// Intenta obtener la final int nofairryacquireshared (int adquirir) {for (;;) {// Get disponible licencias int disponible = getState (); // Obtener licencias restantes int restante = disponible - adquirir; // 1. Si queda menos de 0, regrese restante directamente // 2. Si permanece superior a 0, actualice primero el estado de sincronización y luego returse restante if (restante <0 || compareSetState (disponible, restante)) {return restante; }}}} // Nonfairsync estática Clase final NonfairSync extiende sincronización {privado estático final Long SerialVersionUid = -269418368443567898l; Nonfairsync (int permite) {super (permisos); } // Intenta obtener una licencia protegida int treyCquireshared (int adquirir) {return no fairryacquireshared (adquirir); }} // Synchronizer static static final clase FairSync extiende sincronización {private estático final de serie LongUdVersionUid = 20143338818796000944l; FairSync (int permite) {super (permisos); } // Intenta obtener la licencia protegida int treyCquireshared (int adquirir) {for (;;) {// juzga si hay alguien frente a la cola de sincronización if (hasqueedPredecessors ()) {// Si hay alguna, retorno -1, lo que indica que el intento de obtener el retorno fallido -1; } // Obtener licencias disponibles int disponibles = getState (); // Obtener las licencias restantes int restante = disponibles - adquirir; // 1. Si queda menos de 0, regrese directamente al restante // 2. Si el resto es mayor que 0, el estado de sincronización se actualizará primero y luego se volverá a restante if (restante <0 || compareSetState (disponible, restante)) {return restante; }}}}Cabe señalar aquí que el método TryAcquireshared de Nonfairsync llama directamente el método no FairryAcquireshared, que se encuentra en la sincronización de la clase principal. La lógica del bloqueo de adquisición que no es FAIR es eliminar primero el estado de sincronización actual (el estado sincrónico representa el número de licencias), reste el parámetro del estado de sincronización actual. Si el resultado no es inferior a 0, se demuestra que todavía hay licencias disponibles, entonces el valor del estado de sincronización se actualiza directamente utilizando la operación CAS, y finalmente, el valor del resultado se devolverá independientemente de si el resultado es inferior a 0. Aquí necesitamos comprender el significado del valor de retorno del método de tryacquireshared. Devolver un número negativo significa que la adquisición falló, cero significa que el hilo actual se adquiere con éxito, pero el hilo posterior ya no se puede obtener, y el número positivo significa que el hilo actual se adquiere con éxito y el hilo posterior también se puede obtener. Veamos el código del método de adquisición de Interruption.
// La adquisición de bloqueos en modo interruptible (modo compartido) público Final void adquirido de interrupción (int arg) arroja interruptedException {// Determinar primero si el hilo está interrumpido, de ser así, lanza una excepción if (horthinterrupted ()) {tirar nueva interruptexception (); } // 1. Intente adquirir el bloqueo // número negativo: indica que la adquisición falló // valor cero: indica que el hilo actual se adquiere correctamente, pero el hilo posterior ya no puede obtener // número positivo: indica que el hilo actual se adquiere con éxito, y el hilo posterior también puede obtener éxito si (tryacquircared (arg) <0) {// 2. Si la adquisición falla, ingrese el método doAcquiresharedInterruptiblemente (arg); }}Si el restante devuelto es inferior a 0, significa que la adquisición falló. Por lo tanto, TryAcquireshared (arg) <0 es verdadero, por lo que el método Doacquireshedinterruptualmente se llamará a continuación. Cuando hablamos sobre AQS, envolverá el hilo actual en un nodo y lo pondrá en la cola de la cola de sincronización, y es posible suspender el hilo. Esta es también la razón por la cual los hilos harán cola y bloquearán cuando permanezcan menos de 0. Si el restante restante> = 0 significa que el hilo actual se ha adquirido con éxito. Por lo tanto, TryAcquireshared (Arg) <0 es un flase, por lo que el método DoacquiresharedInterruptualmente ya no se llamará a bloquear el hilo actual. Lo anterior es toda la lógica de la adquisición injusta. Cuando la adquisición justa, solo necesita llamar al método de los procesadores de hasqueada antes de esto para determinar si alguien está haciendo cola en la cola de sincronización. Si es así, el retorno -1 indica directamente que la adquisición falló; de lo contrario, los siguientes pasos continúan como adquisición injusta.
2. Libera la licencia
// Libere una licencia public void Release () {Sync.ReleaseShared (1);}Llamar al método de lanzamiento es liberar una licencia. Su funcionamiento es muy simple, por lo que llamamos al método de desallado de AQS. Echemos un vistazo a este método.
// Operación de bloqueo de lanzamiento (modo compartido) Public Boolean ReleaseShared (int arg) {// 1. Intente liberar el bloqueo if (tryreleasshared (arg)) {// 2. Si el lanzamiento es exitoso, despierta otros hilos doreleasshared (); devolver verdadero; } return false;}El método desagradable de AQS primero llama al método de Tryreleasashared para intentar liberar el bloqueo. La lógica de implementación de este método está en la sincronización de subclase.
La sincronización de la clase estática abstracta extiende AbstractQueueedSynChronizer {... // Intente liberar la operación protegida de tryreleasshared final de booleano (int Lotes) {for (;;) {// Obtener el estado de sincronización actual int couttry = getState (); // más el estado de sincronización actual de la siguiente manera int lo siguiente = corriente + lotes; // Si el resultado de suma es menor que el estado de sincronización actual, se informará un error si (Next <Current) {Throw New Error ("Supero de permiso máximo excedido"); } // Actualice el valor del estado de sincronización en el modo CAS y return True si la actualización es exitosa, de lo contrario continúa en bucle if (compareSetState (actual, next)) {return true; }}} ...}Puede ver que el método Tryreleasasshared utiliza un bucle for para girar. Primero, obtenga el estado de sincronización, agregue los parámetros entrantes y luego actualice el estado de sincronización en CAS. Si la actualización es exitosa, devuelva verdadero y salta del método. De lo contrario, el bucle continuará hasta que tenga éxito. Este es el proceso de liberar la licencia.
3. Escribe un grupo de conexión manualmente
El código semáforo no es muy complicado. La operación de uso común es obtener y liberar una licencia. La lógica de implementación de estas operaciones es relativamente simple, pero esto no obstaculiza la aplicación generalizada de semáforo. A continuación, usaremos Semaphore para implementar un grupo de conexión de base de datos simple. A través de este ejemplo, esperamos que los lectores puedan tener una comprensión más profunda del uso de Semaphore.
clase pública Connectpool {// Tamaño del grupo de conexión Private int tamiz; // COLLECCIÓN DE CONEXIÓN DE LA CONEXIÓN DE DATOS ¡Connects Private [] Connects; // Estado de conexión Flag Boolean [] ConnectFlag; // Número restante de conexiones disponibles Volátil Private int disponible; // semáforo Semafore Semafore Semafore; // Constructor public Connectpool (int tamaño) {this.size = size; this.avelable = tamaño; semáforo = nuevo semáforo (tamaño, verdadero); Conects = New Connect [size]; ConnectFlag = new Boolean [tamaño]; initConnects (); } // Inicializar la conexión privada void initConnects () {// Generar un número especificado de conexiones de base de datos para (int i = 0; i <this.size; i ++) {conects [i] = new Connect (); }} // Obtener conexión de base de datos Private Synchronized Connect getConnect () {for (int i = 0; i <ConnectFlag.length; i ++) {// Transferir la colección para encontrar conexiones no utilizadas if (! ConnectFlag [i]) {// Establezca la conexión a en USED ConnectFlag [i] = True; // Resta el número de conexiones disponibles disponibles--; System.out.println ("【"+thread.currentThread (). GetName ()+"】 para obtener el número de conexiones restantes:"+disponible); // devuelve la referencia de conexión de retorno connects [i]; }} return null; } // Obtener una conexión public Connect OpenConnect () lanza interruptedException {// Obtener licencia SEMAPHORE.ACQUIRE (); // Obtener la conexión de la base de datos return getConnect (); } // Liberar una conexión public sincronizada sincronizada Void Release (Connect Connect) {for (int i = 0; i <this.size; i ++) {if (Connect == Connects [i]) {// Establezca la conexión a no usado ConnectFlag [i] = false; // Agregar 1 número de conexión disponible; System.out.println ("【"+thread.currentThread (). GetName ()+"] para liberar el número de conexión restante:"+disponible); // Licencia de liberación Semaphore.Release (); }}} // Agregar número de conexiones disponibles public int disponible () {return disponible; }}Código de prueba:
Public Class TestThread extiende el subproceso {privado static conectionpool Pool = new Connectpool (3); @Override public void run () {try {Connect Connect = Pool.openconnect (); Hilt.sleep (100); // tomar una piscina de descanso. Release (conectar); } catch (InterruptedException e) {E.PrintStackTrace (); }} public static void main (string [] args) {for (int i = 0; i <10; i ++) {new testThread (). start (); }}}Resultados de la prueba:
Utilizamos una matriz para almacenar referencias a las conexiones de la base de datos. Al inicializar el grupo de conexión, llamaremos al método InitConnects para crear un número especificado de conexiones de bases de datos y almacenar sus referencias en la matriz. Además, hay una matriz del mismo tamaño para registrar si la conexión está disponible. Siempre que un subproceso externo solicite una conexión, primero llame al método Semaphore.Acquire () para obtener una licencia, luego configure el estado de conexión en uso y finalmente devuelva la referencia a la conexión. El número de licencias está determinado por los parámetros pasados durante la construcción. El número de licencias se reduce en 1 cada vez que se llama al método Semaphore.Acquire (). Cuando el número se reduce a 0, significa que no hay conexión disponible. En este momento, si otros hilos lo obtienen nuevamente, se bloqueará. Cada vez que un hilo libera una conexión, se llamará a SemAphore.Release () para liberar la licencia. En este momento, el número total de licencias aumentará nuevamente, lo que significa que el número de conexiones disponibles ha aumentado. Luego, el hilo previamente bloqueado se despertará y continuará obteniendo la conexión. En este momento, puede obtener con éxito la conexión obteniéndola nuevamente. En el ejemplo de prueba, se inicializa un grupo de conexión de 3 conexiones. Podemos ver en los resultados de la prueba que cada vez que un hilo obtiene una conexión, el número de conexiones restantes se reducirá en 1. Cuando el hilo se reduzca a 0, otros hilos ya no pueden obtenerlo. En este momento, debe esperar a que un hilo libere la conexión antes de continuar obteniéndola. Puede ver que el número de conexiones restantes siempre cambia entre 0 y 3, lo que significa que nuestra prueba fue exitosa.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.