如何使线程等到事情发生而不阻塞其他事件

时间:2015-11-27 12:56:52

标签: java multithreading wait notify

我正在使用java中的线程进行理发店模拟。理发店包括一间带n把椅子的候诊室和一间带多把理发椅的理发室。如果没有顾客服务,理发师就会入睡。如果顾客进入理发店并且所有椅子都被占用,则顾客离开商店。如果理发师很忙但是椅子可用,则顾客坐在其中一把免费椅子上。如果理发师睡着了,顾客会叫醒理发师。

到目前为止,我的代码运行良好(我认为),但有一个错误。在这里客户经过一段随机的毫秒后去理发店,可以去那里几次。问题是,如果客户已经在理发店,那么在服务结束之前它不能再去那里,这意味着,我需要阻止客户线程,直到理发师完成剪头发,这就是我的代码失败的地方。我很确定它必须用wait()和notify()完成,但是当我这样做时,我搞乱了其余的线程,可能我做错了。那我怎么能同步呢?在此先感谢:)

这是我的代码:

Main.java:

public class Main {

public static int tiempoSimulacion;

public static Logger logger = Logger.getLogger("ccia.labarberia");
static {
    logger.setLevel(Level.OFF)
    //logger.setLevel(Level.WARNING)
    ;}

public static void main(String[] args) throws InterruptedException {
    Scanner sc = new Scanner(System.in);

    int nBarberos = sc.nextInt();
    int nClientes = sc.nextInt();
    tiempoSimulacion = sc.nextInt();

    Cliente.distribucionNormal = new NormalDistribution(sc.nextInt(),sc.nextInt());

    Barbero.distribucionExponencial = new ExponentialDistribution(sc.nextInt());

    Barberia b = Barberia.getBarberia(); // La Barberia sigue el patrón Singleton

    Cliente.barberia = b;
    Barbero.barberia = b;
    b.setNumeroSillas(sc.nextInt());
    sc.close();

    Barbero[] barberos = new Barbero[nBarberos];
    for (int i=1; i<=nBarberos; i++){
        barberos[i-1] = new Barbero(i);
        barberos[i-1].start();
    }
    b.setBarberos(barberos);

    Thread[] clientes = new Thread[nClientes];
    for (int j=1; j<=nClientes; j++){
        clientes[j-1] = new Thread(new Cliente(j));
        clientes[j-1].start();
    }

    Thread.sleep(tiempoSimulacion*1000);

    for (int j=0; j<nClientes; j++){
        clientes[j].interrupt();
    }

    for (int i=0; i<nBarberos; i++){
        barberos[i].interrupt();
    }

    for (int j=0; j<nClientes; j++){
        clientes[j].join();
    }

    for (int i=0; i<nBarberos; i++){
        barberos[i].join();
    }


}

}

Client.java:

public class Cliente extends Thread{

public static Barberia barberia;
public static NormalDistribution distribucionNormal;

private int id;

public Cliente (int id) {
    this.id = id;
    System.out.println("El cliente " + id + " se ha creado.");
}

public int identificador() {
    return this.id;
}

@Override
public void run() {
    try{
        while (!Thread.interrupted()) {
            try {
                Thread.sleep((long)distribucionNormal.sample() );
            } catch (IllegalArgumentException e) {

            }
            this.irABarberia();
        }
    }catch (InterruptedException e) {
        System.out.println("El cliente " + this.id + " ha sido destruido.");
    }
}

public void irABarberia() throws InterruptedException{
    barberia.add(this);
}
}

Barber.java:

public class Barbero extends Thread{

private static final int OFFSET = 64;
public static Barberia barberia;
public static ExponentialDistribution distribucionExponencial;
private char id;

public Barbero(int i) {
    this.id = (char) (OFFSET + i); 
    System.out.println("El barbero " + this.id + " se ha creado.");
}

public char identificador() {
    return this.id;
}

@Override
public void run() {
    try {
        while(!Thread.interrupted()) {
            barberia.cortarPelo(this);  
        } 
    } catch (InterruptedException ex) {
        System.out.println("El barbero " + this.id + " ha sido destruido.");
    }

}
}

Barbershop.java:

public class Barberia {

private static Barberia mBarberia;
private static int numSillas;
private Barbero[] barberos;
LinkedList<Cliente> listaClientes;

protected Barberia() {
    listaClientes = new LinkedList<Cliente>();
}

public static Barberia getBarberia() {
    if (mBarberia == null) {
        mBarberia = new Barberia();
    }
    return mBarberia;
}

public void setNumeroSillas(int sillas) {
    numSillas = sillas;
}

public void setBarberos(Barbero[] b) {
    this.barberos = new Barbero[b.length];
    for (int i=0; i<this.barberos.length; i++) {
        this.barberos[i] = b[i];
    }
}

public void cortarPelo(Barbero barbero) throws InterruptedException{

    Cliente cliente;
    synchronized (listaClientes) {
        while(listaClientes.size()==0) {
            System.out.println("El barbero " + barbero.identificador() + " se pone a dormir.");
            listaClientes.wait();
        }
        cliente = listaClientes.poll();

    }    
    System.out.println("El barbero " + barbero.identificador() + " atiende al cliente " + cliente.identificador() + ".");
    Thread.sleep((long)Barbero.distribucionExponencial.sample() );
    System.out.println("El barbero " + barbero.identificador() + " ha cortado el pelo al cliente " + cliente.identificador() + ".");

}

public void add (Cliente cliente) throws InterruptedException{
     System.out.println("El cliente " + cliente.identificador() + " llega a la barbería.");
     synchronized (listaClientes) {
         if(listaClientes.size() == numSillas) {
             System.out.println("El cliente " + cliente.identificador() + " se marcha sin ser atendido."); 
         } else {
             listaClientes.offer(cliente);

             if(listaClientes.size()>0 && listaClientes.size() <= barberos.length){
                 listaClientes.notify();
             } else {
                 System.out.println("El cliente " + cliente.identificador() + " se sienta en una silla de espera.");
             }  
         }    
     }
}
}

1 个答案:

答案 0 :(得分:-1)

您想要实现的目标,您可以使用同步功能。当多个线程正在同步同一个对象时,java会处理您自然寻求的行为:

  • 一次只能有一个线程访问同步锁。该 别人等了
  • 当锁定空闲时,所有等待的线程都会尝试访问它。无需使用等待,加入或通知。

    这是一个例子,实现你所谈论的行为:几个客户试图同时访问理发店的同一个席位。

理发店和主要计划:

public class BarberShop
{

// The main programm
public static void main(final String[] args)
{
    // creating a Barbershopand its clients
    final BarberShop shop = new BarberShop();
    final BarberClient c1 = new BarberClient(shop, "Ian", 5);

    final BarberClient c2 = new BarberClient(shop, "Chewy", 10);

    final BarberClient c3 = new BarberClient(shop, "Luke", 3);

    // all clients Try to go to the barbershop concurrently
    c1.launchGoToBarberThread();
    c2.launchGoToBarberThread();
    c3.launchGoToBarberThread();

    // Example output :
    // Chewy Waiting for barbershop
    // Luke Waiting for barbershop
    // Ian Waiting for barbershop
    // Chewy Entered Barber shop at Fri Nov 27 14:32:38 CET 2015
    // Chewy Exiting Barber shop at Fri Nov 27 14:32:43 CET 2015
    // Ian Entered Barber shop at Fri Nov 27 14:32:43 CET 2015
    // Ian Exiting Barber shop at Fri Nov 27 14:32:45 CET 2015
    // Luke Entered Barber shop at Fri Nov 27 14:32:45 CET 2015
    // Luke Exiting Barber shop at Fri Nov 27 14:32:47 CET 2015

}
}

客户在商店同步:

import java.util.Date;

// client class implementing the concurrency behaviour around the seat.
public class BarberClient
{

// the shop we synchronize on. Let's say it has only one seat and we synchronize on this.
private final BarberShop barberShop;

private final String     name;      // a name

private final int        beardSize; // the size of the beard to represent the time in the seat

private final Thread     thread;    // the thread that will actually be launched.

// constructor with name and beard size.
public BarberClient(final BarberShop pBarberShop,
                    final String pName,
                    final int pBeardSize)
{
    barberShop = pBarberShop;
    name = pName;
    beardSize = pBeardSize;

    // create the thread that just try to go to the barber...
    thread = new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            try
            {
                goToBarber();

            } catch (final InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    });
}

// method concurrently trting to go to the shop
private void goToBarber() throws InterruptedException
{
    System.out.println(name + "\tWaiting for barbershop");// display client is waiting

    // Synchronize on the shop. It means you can seat only if nobody is on the seat. And waits for the seat
    // being free.
    synchronized (barberShop)
    {
        // We entered the synchronization block (= accessed to the seat).
        // NOBODY else can access the seat while we are here.

        // log enter, exit time, and wait for some time for the barber to do his job...
        System.out.println(name + "\tEntered Barber shop at " + new Date(System.currentTimeMillis()));
        Thread.sleep(500 * beardSize);
        System.out.println(name + "\tExiting Barber shop at " + new Date(System.currentTimeMillis()));
    }
    // now we have leave the synchronization block (and the seat). Anybody can go.
}

// method to launch the thread.
public void launchGoToBarberThread()
{
    thread.start();
}
}

我希望它能帮助您实施行为。 您可以在此处获得有关同步的一些信息:https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html