Método de comunicación de datos entre varios subprocesos

Método de comunicación de datos entre varios subprocesos

Modelo productor consumidor

El problema de producción / consumidor es un problema de subprocesos múltiples muy típico, y los objetos involucrados incluyen productores, consumidores, almacenes y productos. La relación entre ellos es la siguiente:

  • El productor solo produce cuando el almacén no está lleno y detiene la producción cuando el almacén está lleno.
  • Los consumidores solo pueden consumir cuando hay productos en el almacén y esperar cuando el almacén está vacío.
  • Cuando el consumidor descubre que el almacén no tiene productos para consumir, se le notificará al productor para que produzca.
  • Cuando un productor produce un producto consumible, debe notificar al consumidor que espera que lo consuma.

Implementación de codificación

Categoría de almacén, el producto son los datos de atributo del almacén.

//临界资源
public class Basket {
    
    
	private volatile Object data;
	//生产者向仓库中存放数据
	public synchronized void product(Object data) {
    
    
		//如果仓库中有数据,则生产者进入阻塞等待,直到其它线程唤醒
		while(this.data!=null)
			try {
    
    
				this.wait();
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
		//如果没有数据则进行生产操作
		this.data=data;
		System.out.println(Thread.currentThread().getName()+"生产了一个日期"+this.data);
		this.notifyAll();//唤醒在当前对象上处于wait的所有线程
	}
	//消费者从仓库中消费数据
	public synchronized void consume() {
    
    
		//如果仓库中没有数据,则消费者进入阻塞等待,直到其它线程唤醒
		while(this.data==null)
			try {
    
    
				this.wait();
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
		//如果有数据data!=null,则执行消费操作
		System.out.println(Thread.currentThread().getName()+"消费了一个数据"+this.data);
		this.data=null;
		this.notifyAll();  //唤醒在当前对象上处于wait的所有线程
	}
}

El hilo productor es responsable de producir productos y compartir almacenes con los consumidores.

public class Producer extends Thread {
    
    
	private Basket basket;
	//通过构造器传入对应的basket对象
	public Producer(Basket basket) {
    
    
		this.basket=basket;
	}
	@Override
	public void run() {
    
    
		//生产20次日期对象
		for(int i=0;i<20;i++) {
    
    
			Object data=new Date();  //生产者生产的具体产品
			basket.product(data);
		}
	}
}

El hilo consumidor se encarga de consumir el producto y compartir el almacén con el productor.

public class Consumer extends Thread {
    
    
	private Basket basket;

	public Consumer(Basket basket) {
    
    
		this.basket = basket;
	}

	@Override
	public void run() {
    
    
		// 消费20次日期对象
		for (int i = 0; i < 20; i++) {
    
    
			basket.consume();
		}
	}
}

Los productores y los consumidores comparten el mismo espacio de almacenamiento en el mismo período de tiempo. Los productores agregan productos al espacio de almacenamiento y los consumidores eliminan productos del espacio de almacenamiento. Cuando el espacio de almacenamiento está vacío, el consumidor se bloquea y cuando el espacio de almacenamiento está lleno Cuando el productor está bloqueado.

Por qué utilizar el modelo productor / consumidor

En el mundo de los subprocesos, el productor es el subproceso que produce los datos y el consumidor es el subproceso que consume los datos. En el desarrollo de subprocesos múltiples, si la velocidad de procesamiento del productor es muy rápida, pero la velocidad de procesamiento del consumidor es muy lenta, entonces el productor debe esperar a que el consumidor termine de procesar antes de continuar produciendo datos. De la misma manera, si el poder de procesamiento del consumidor es mayor que el del productor, entonces el consumidor debe esperar al productor. Para solucionar este problema del desequilibrio en la capacidad de producción y consumo, existe un modelo de productor y consumidor.

Ventajas del modelo productor / consumidor

1. Desacoplamiento. Debido a que hay un búfer adicional, el productor y el consumidor no se llaman directamente entre sí. Esto es fácil de pensar. De esta manera, el código del productor y el consumidor no se afectarán entre sí. De hecho, el productor y el consumidor no se verá afectado. El fuerte acoplamiento entre el consumidor y el consumidor se deshace, y se convierte en el débil acoplamiento entre el productor y el búfer / consumidor y el búfer

2. Mejorar la velocidad general de procesamiento de datos equilibrando las capacidades de procesamiento de productores y consumidores Esta es una de las ventajas más importantes del modelo productor / consumidor. Si el consumidor obtiene los datos directamente del productor, si la velocidad de producción del productor es muy lenta, pero la velocidad de consumo del consumidor es muy rápida, entonces el consumidor tendrá que tomar la porción de tiempo de la CPU y esperar allí por nada. Con el modelo de productor / consumidor, el productor y el consumidor son dos entidades concurrentes independientes. El productor simplemente arroja los datos producidos al búfer, independientemente del consumidor; el consumidor también es del búfer Solo obtenga los datos y usted no ' No hay que preocuparse por el productor, si el búfer está lleno, no se producirá, y si el búfer está vacío, no se realizará ningún consumo, por lo que la capacidad de procesamiento del productor / consumidor alcanza un equilibrio dinámico.

El papel del modelo productor / consumidor

  • Simultaneidad de soporte
  • Desacoplamiento
  • Apoyar la disponibilidad desigual

Los métodos de llamada como esperar / notificar deben estar dentro del objeto de hilo actual, como en el método sincronizado

único

Asegúrese de que solo haya una instancia en la VM

  • Modo hombre hambriento
    • Constructor privado
    • Instancia privada de Singleton estático = new Singleton ();
    • Método estático compartido
  • Modo de hombre perezoso
    • Constructor privado
    • Propiedades estáticas privadas, no cree objetos directamente
    • Método estático compartido
public class Singleton {
    
    
	private Singleton() {
    
    }
	private static Singleton instance;
	public static Singleton getInstance() {
    
    
		if(instance==null)
			instance=new Singleton();   //当第一次使用对象时才进行创建
		return instance;
	}
}

Puede haber múltiples creaciones del objeto, cómo resolverlo

public class Singleton {
    
    
	private Singleton() {
    
    }
	private static Singleton instance;
	public synchronized static Singleton getInstance() {
    
    
		if(instance==null)
			instance=new Singleton();   //当第一次使用对象时才进行创建
		return instance;
	}
}

Generalmente, no se recomienda utilizar un mecanismo de procesamiento de bloqueo más granular, ya que la concurrencia se verá afectada

Modo perezoso de doble detección

  • Crea objetos bajo demanda para evitar operaciones de creación inútiles

  • A salvo de amenazas

public class Singleton{
    
     
    private Singleton(){
    
    }
    private static Singleton instance=null;
    public static Singleton getInstance(){
    
    
    	if(instance==null){
    
    
    		synchronized(Singleton.class){
    
    
    			if(instance==null)
    	    	    instance=new Singleton();
    	    }
    	 }   
        return instance;
    }
}

Uso de bloqueo

Lock es una herramienta de sincronización de subprocesos introducida en Java 1.5.Se utiliza principalmente para controlar recursos compartidos en varios subprocesos. En esencia, Lock es solo una interfaz. La sincronización se puede lograr definiendo explícitamente un objeto de bloqueo de sincronización. Puede proporcionar una gama más amplia de operaciones de bloqueo que las sincronizadas y admite varios objetos de condición relacionados.

  • void lock (); Intenta adquirir el bloqueo, regresa si la adquisición es exitosa, de lo contrario bloquea el hilo actual

void lockInterruptiblemente () arroja InterruptedException; intenta adquirir el bloqueo, el hilo se interrumpe antes de adquirir con éxito el bloqueo, luego deja de adquirir el bloqueo y lanza una excepción

boolean tryLock (); Intenta adquirir el bloqueo, devuelve verdadero si el bloqueo se adquiere con éxito, de lo contrario devuelve falso

boolean tryLock (tiempo largo, unidad TimeUnit) intenta adquirir el bloqueo. Si el bloqueo se adquiere dentro del tiempo especificado, devuelve verdadero, de lo contrario devuelve falso. Si se interrumpe antes de adquirir el bloqueo, se lanza una excepción.

  • Desbloqueo vacío (); suelte el bloqueo
  • Condition newCondition (); devuelve la variable de condición del bloqueo actual. La función similar a notificar y esperar se puede realizar a través de la variable de condición. Un bloqueo puede tener múltiples variables de condición

Lock tiene tres clases de implementación, una es ReentrantLock y las otras dos son las dos clases internas estáticas ReadLock y WriteLock en la clase ReentrantReadWriteLock.

Uso: Al acceder a recursos compartidos (mutuamente excluyentes) con subprocesos múltiples, bloquee antes del acceso y desbloquee después del acceso. Se recomienda que la operación de desbloqueo se coloque en el bloque final.

bloqueo ReentrantLock final privado = new ReentrantLock ();

En el método específico lock.lock () intente {} finalmente {lock.unlock}

Lock lock=new ReentrantLock();//构建锁对象
try{
    
    
	lock.lock();//申请锁,如果可以获取锁则立即返回,如果锁已经被占用则阻塞等待
	System.out.println(lock);//执行处理逻辑
} finally{
    
    
	lock.unlock();//释放锁,其它线程可以获取锁
}

Ejemplo 1: Inicie 4 subprocesos, realice 50 operaciones de suma y resta en un número int, requiriendo 2 sumas y 2 restas para garantizar la seguridad del subproceso de la salida

public class OperNum {
    
    
	private int num=0;	
	private final static Lock lock=new ReentrantLock();  //构建锁对象
	public void add() {
    
    
		try {
    
    
			lock.lock();  //申请加锁操作,如果能加上则立即返回,否则阻塞当前线程
			num++;
			System.out.println(Thread.currentThread().getName()+"add..."+num);
		} finally {
    
    
			lock.unlock(); //具体实现采用的是重入锁,所以持有锁的线程可以多次申请同一个锁,但是申请加锁次数必须和释放锁的次数一致
		}
	}
	public void sub() {
    
    
		try {
    
    
			lock.lock();
			num--;
			System.out.println(Thread.currentThread().getName()+"sub..."+num);
		} finally {
    
    
			lock.unlock();
		}
	}
}

Interfaz de condición

La condición es una interfaz proporcionada en el paquete juc. Se puede traducir en un objeto de condición, su función es que el hilo espere primero, y cuando una determinada condición se cumple externamente, el hilo en espera se despierta a través del objeto de condición.

void await () arroja InterruptedException; Deje que el hilo entre en espera. Si otros hilos llaman notificar / notificar a todos del mismo objeto Condición, el hilo en espera puede despertarse. Suelta la cerradura

señal void (); despierta el hilo en espera

void signalAll (); despierta todos los hilos

Puntos especiales de la condición de uso:

  • Cuando se llama a condition.await () para bloquear el hilo, el bloqueo se liberará automáticamente, sin importar cuántos lock.lock () se llame, entonces el hilo bloqueado en el método lock.lock () puede adquirir el bloqueo
  • Cuando se llama a condition.signal () para despertar el hilo, continuará ejecutándose en la última posición bloqueada, y el bloqueo se volverá a adquirir automáticamente de forma predeterminada (tenga en cuenta que el número de bloqueos adquiridos durante el bloqueo es el mismo)

Supongo que te gusta

Origin blog.csdn.net/qq_43480434/article/details/114153429
Recomendado
Clasificación