【问题标题】:Multithreading programming in Java, using semaphoresJava中的多线程编程,使用信号量
【发布时间】:2012-12-22 20:04:30
【问题描述】:

我正在学习 Java 多线程,但遇到问题,我无法理解信号量。如何按此顺序执行线程?例如:在 image1 上:第 5 线程开始运行,然后第 1 和第 2 线程完成执行。

图 2:


图片1:

我现在上传图片以便更好地理解。 :))

【问题讨论】:

  • 如果你想在 N 个其他任务之后执行某个任务,你可以使用CountDownLatch
  • 在第一张图中,线程不是并行运行,而是顺序运行。每个线程等到其他线程完成,然后创建一个新线程。第二张图一次创建所有线程,但它们不同步。
  • 你不应该真正尝试这样做。直接跳过并阅读实践中的并发。
  • @RomanC 你可能误读了这个“图像”。它们不是 UML 图。在这种情况下,时间线不是垂直的而是水平的。正如 OP 所说 “在 image1 上,第 5 线程开始运行,然后第 1 和第 2 线程完成”
  • @Pshemo 并没有说第6个线程什么时候开始运行,可能会省略图片编号,最好贴SSCCE或者UML会更好。

标签: java multithreading semaphore wait notify


【解决方案1】:

使用同步,以便每个线程一次进入该方法或该部分代码。如果你想

public class CountingSemaphore {
  private int value = 0;
  private int waitCount = 0;
  private int notifyCount = 0;

  public CountingSemaphore(int initial) {
    if (initial > 0) {
      value = initial;
    }
  }

  public synchronized void waitForNotify() {
    if (value <= waitCount) {
      waitCount++;
      try {
        do {
          wait();
        } while (notifyCount == 0);
      } catch (InterruptedException e) {
        notify();
      } finally {
        waitCount--;
      }
      notifyCount--;
    }
    value--;
  }

  public synchronized void notifyToWakeup() {
    value++;
    if (waitCount > notifyCount) {
      notifyCount++;
      notify();
    }
  }
}

这是一个计数信号量的实现。它维护计数器变量“value”、“waitCount”和“notifyCount”。如果 value 小于 waitCount 并且 notifyCount 为空,这会使线程等待。

您可以使用 Java 计数信号量。从概念上讲,信号量维护一组许可。如有必要,每个 acquire() 都会阻塞,直到获得许可,然后再接受它。每个 release() 添加一个许可,可能会释放一个阻塞的获取者。但是,没有使用实际的许可对象; Semaphore 只是对可用数量进行计数并采取相应措施。

信号量通常用于限制可以访问某些(物理或逻辑)资源的线程数。例如,这是一个使用信号量来控制对项目池的访问的类:

 class Pool {
   private static final MAX_AVAILABLE = 100;
   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

   public Object getItem() throws InterruptedException {
     available.acquire();
     return getNextAvailableItem();
   }

   public void putItem(Object x) {
     if (markAsUnused(x))
       available.release();
   }

   // Not a particularly efficient data structure; just for demo

   protected Object[] items = ... whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];

   protected synchronized Object getNextAvailableItem() {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (!used[i]) {
          used[i] = true;
          return items[i];
       }
     }
     return null; // not reached
   }

   protected synchronized boolean markAsUnused(Object item) {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (item == items[i]) {
          if (used[i]) {
            used[i] = false;
            return true;
          } else
            return false;
       }
     }
     return false;
   }

 }

在获取项目之前,每个线程必须从信号量中获取许可,以确保项目可供使用。当线程完成该项目时,它会返回到池中,并向信号量返回一个许可,允许另一个线程获取该项目。请注意,在调用 acquire() 时不会持有同步锁,因为这会阻止项目返回到池中。信号量封装了限制对池的访问所需的同步,与维护池本身一致性所需的任何同步分开。

初始化为 1 的信号量,并且使用时最多只有一个可用的许可,可以用作互斥锁。这通常被称为二进制信号量,因为它只有两种状态:一个可用许可,或零个可用许可。当以这种方式使用时,二进制信号量具有属性(与许多 Lock 实现不同),“锁”可以由所有者以外的线程释放(因为信号量没有所有权的概念)。这在一些专门的上下文中很有用,例如死锁恢复。

【讨论】:

    【解决方案2】:

    通常在 java 中使用互斥体(也称为监视器),它禁止两个或多个线程访问受该互斥体保护的代码区域

    该代码区域是使用sychronized 语句定义的

    sychronized(mutex) {
     // mutual exclusive code begin
     // ...
     // ...
     // mutual exclusive code end
    
    }
    

    其中互斥锁定义为例如:

    Object mutex = new Object();
    

    为了防止任务启动,您需要在 java.util.concurrency 包中定义的高级技术,例如屏障。

    但首先要让自己对 synchronized 声明感到满意。

    如果你认为你会经常在java中使用多线程,你可能想阅读

    《Java 并发实践》

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      相关资源
      最近更新 更多