信号量用于线程中的信令不工作(并发问题)

时间:2014-11-08 11:37:24

标签: java multithreading concurrency signals semaphore

在这个项目中,我试图在使用信号量作为信令的线程之间进行一些并发,但是并发性根本不起作用。我只能使用获取和发布,并且不允许使用synchronized关键字方法。我阅读了无数的网页,并说它

//做点什么

获取()

release()

//做点什么

我理解但在这个程序中我试图用线程之间的信号量测试信号,例如用户请求存款和出纳员应该说存款已完成。但是我的信号(信号量)不能正常工作,因为我想按顺序打印例如

我需要存款 存款已完成

相反,我得到了这个

Customer0created

我需要存款

我已撤回< ---

Customer0从主要

加入

Teller0created

你想退出吗? < ----(无序)

存款已完成

无论我如何对它们进行重新排序,或者我读取了多少通信线程的信号量信号都无效。

 [code]import java.util.concurrent.Semaphore;

 public class Threads {


 private static Semaphore depositTransaction = new Semaphore (1, true);
 private static Semaphore withdrawal = new Semaphore (1, true);

 public static void main(String[] args) 

   {
    final int customerThreads = 1;
    final int tellerThreads = 1;
    final int loanThreads = 1;

    Customer thr[] = new Customer[customerThreads]; //  
   Thread cThread[] = new Thread[customerThreads]; //         
    for (int i= 0; i < customerThreads; i++)
    {
        thr[i]= new Customer(i);
        cThread[i] = new Thread(thr [i]);
        cThread[i].start();

    }
    for ( int i = 0; i < customerThreads; i++ )
    {
        try {
            cThread[i].join();
            System.out.println("Customer"+i + "joined from main");

        }
        catch (InterruptedException e)
        {

        }
    }

    Teller thr1[] = new Teller[tellerThreads];
    Thread tThread[] = new Thread[tellerThreads];
    for (int b = 0; b< tellerThreads; b++)
    {
        thr1[b] = new Teller(B)/>;
        tThread[b]= new Thread(thr1 [b]);
        tThread[b].start();
    }              
}   

static class Customer implements Runnable
 {
private int customerNumber = 0;
private int balance = 0;

Customer(int cn)
{
   this.customerNumber = cn;
   balance = 1000;
   System.out.println("Customer"+ customerNumber + "created");
}

public void run()
{

    try
    {

     System.out.println("I need to deposit");
    depositTransaction.acquire();// signal

    }
    catch(InterruptedException e)
    {
        Thread.currentThread().interrupt();
        e.printStackTrace();
    }

   withdrawal.release();
   System.out.println("I have withdrawn");
}       
}

static class Teller implements Runnable 
{
private int tellerNumber = 0;

Teller(int tn)
{
    this.tellerNumber = tn;
    System.out.println("Teller"+ tellerNumber +"created");
}

public void run()
{
    try 
    {

    System.out.println("You wanna withdrawal?"); 
    withdrawal.acquire();

    }
    catch(InterruptedException e)
    {
        Thread.currentThread().interrupt();

    }
    depositTransaction.release();
    System.out.println("Deposit is complete");
}

} 

}[/code]

3 个答案:

答案 0 :(得分:0)

您没有正确使用信号量来做您想做的事情。当我得到它时,你想要启动客户线程,然后阻止直到柜员线程完成然后完成客户线程。

现在你的信号量几乎没有。它们会阻止多个客户线程同时运行,但在获取/发布块中,没有任何反应。

如果要在客户和柜员之间进行同步,两个类都需要使用相同的信号量

我建议的是:

remove the join operation for now
create the depositTransaction semaphore with count 0, so the first acquire will block.
Start a customer thread
The thread will block waiting for a deposit
Start a teller thread
make the deposit and release the depositTransaction semaphore
the customer thread will unblock
you can now join both threads

编辑:

我不认为你为每个动作增加大量信号量的想法是个好主意。你最终会遇到复杂的锁定和死锁。我建议的是限制信号量的数量并在线程之间实现消息。信号量将告诉另一个(客户告诉Teller,反之亦然)在推送消息后检查消息。

Start customer thread
push message that customer is waiting
signal for new customer request
wait for teller signal
Start teller thread
acquire sem for new customer request
check message
do stuff
signal customer that stuff is done

消息将是“撤回客户0”或您要实施的任何其他操作

答案 1 :(得分:0)

建议您查看其中一个标准示例并重新编写代码。信号量非常容易使用,我们需要做的就是在线程访问共享资源时获取锁,并在完成时释放锁。

有一个很好的例子,生产者和消费者线程在这里保护共享资源。 Semaphore Example with a Producer and Consumer thread

答案 2 :(得分:0)

这是一个使用信号量来播放乒乓球的程序。它与您的目标非常相似。这个程序有一个打印PING的线程,另一个打印PONG。它使用信号量来确保首先打印PING,然后是PONG,然后是PING等。

注意这个程序如何使用两个信号量,并且它在零时启动两个信号量。这意味着当线程在其上调用acquire()时,它们将被阻塞。你一直在使用一个值,这意味着两个线程都不会阻塞,两者都会抢先一步。

现在所有线程都已被阻止,我们需要启动其中一个。我们向信号量发送一个'release()'信号,表示我们想要启动的线程。这将使信号量增加1,并且在继续执行打印PING或PONG的所有重要工作之前,在acquire()中被阻塞的线程将被唤醒并再次减少它。

请记住以下有关信号量的信息:

  • 信号量包含整数值(称为许可计数)
  • acquire()将阻塞,直到整数值大于零;当大于零时,在退出
  • 之前,计数将减1
  • release()永远不会阻止。它只会将整数值递增1,并且副作用会唤醒在对acquire()的调用中被阻止的任何方法。

因此,对于乒乓球游戏来说:(ascii art下面滚动到右边)

            
   s1=0             -- release() -->  s1=1                                          s1=0
   s2=0                               s2=0                                          s2=1
   thread1=blocked                    thread1 runs      -- calls s2.release() -->   thread1 blocked
   thread2=blocked                    thread2=blocked                               thread2 runs

注意s1和s2的值如何在0和1之间振荡,但我们不允许它们同时具有值​​1。如果它们都等于1,那么thread1和thread2都可以同时运行。这将被称为竞争条件,因为它们的执行顺序是不可预测的。

public class PingPong {

    public static void main( String[] args ) throws InterruptedException {
        final Semaphore s1 = new Semaphore(0);
        final Semaphore s2 = new Semaphore(0);

        final AtomicInteger countDown = new AtomicInteger( 10 );
        Thread threadA = new Thread() {
            public void run() {
                try {
                    System.out.println("threadA started");
                    while (countDown.get() > 0) {
                        s1.acquire();

                        System.out.println( "PING" );

                        s2.release();

                        countDown.decrementAndGet();
                    }
                } catch ( InterruptedException e ) {
                    e.printStackTrace();
                }

                System.out.println("threadA finished");
            }
        };

        Thread threadB = new Thread() {
            public void run() {
                try {
                    System.out.println("threadB started");
                    while (countDown.get() > 0) {
                        s2.acquire();

                        System.out.println( "PONG" );

                        s1.release();

                        countDown.decrementAndGet();
                    }
                } catch ( InterruptedException e ) {
                    e.printStackTrace();
                }

                System.out.println("threadb finished");
            }
        };

        threadA.start();
        threadB.start();


        s1.release();
    }
}