欢迎访问宙启技术站
智能推送

Java线程之间的共享与协作详解

发布时间:2023-05-15 05:24:31

Java中的线程通常需要协作和共享资源。在这篇文章中,我们将学习如何共享数据以及如何在多个线程之间协作。我们将从基本的概念开始,从一个简单的示例开始,然后深入研究线程的同步和互斥机制。最后,我们将介绍Java5中的并发工具包。

线程之间的数据共享

Java中的线程可以访问自己创建的对象。如果你创建了一个对象并将其传递给多个线程,则这些线程可以同时访问该对象。

例如,考虑以下代码片段:

class MyObject {
   private int counter = 0;
   public int getCounter() {
      return counter;
   }
   public void incrementCounter() {
      counter++;
   }
}

在这里,我们定义了一个类MyObject,它有一个计数器字段和两个方法:getCounter()和incrementCounter()。注意,counter字段是私有的,这意味着它只能由该类中的方法访问。

如果我们创建了多个线程来访问MyObject对象,则它们可以同时访问counter字段,因为它是MyObject的实例字段。例如,我们可以使用以下代码片段:

MyObject obj = new MyObject();
Runnable r = () -> {
   for (int i = 0; i < 1000000; i++) {
      obj.incrementCounter();
   }
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(obj.getCounter());

这段代码创建了两个线程,它们都调用obj.incrementCounter()方法1000000次。最后,我们打印出了计数器的最终值。由于两个线程同时增加计数器的值,最终的计数器值应该是2000000。

线程之间的协同

除了共享数据外,线程之间还需要协作。例如,一个线程可能需要等待另一个线程完成某项任务。在Java中,你可以使用以下构造来实现线程之间的协作:

wait():使线程等待某些条件满足。

notify():通知等待的线程某些条件已经满足。

notifyAll():通知等待的线程所有条件已经满足。

这些方法只能在同步代码块中调用,而同步代码块要么使用synchronized方法,要么使用synchronized块。

让我们看一个例子。假设我们有两个线程,一个生产者和一个消费者,它们共享一个队列。

class MyQueue {
   private List<String> buffer = new ArrayList<>();
   public synchronized void put(String value) {
      buffer.add(value);
      notify();
   }
   public synchronized String take() {
      while (buffer.isEmpty()) {
         try {
            wait();
         } catch (InterruptedException e) {
            // ignore
         }
      }
      return buffer.remove(0);
   }
}

在这里,我们定义了一个类MyQueue,它有一个buffer字段和两个方法:put()和take()。put()方法允许生产者将元素添加到队列中,而take()方法允许消费者从队列中获取元素。注意,put()和take()方法都是synchronized方法,这将使这些方法成为临界区域,这些方法在同时只能被一个线程执行。此外,put()和take()方法都包含wait()和notify()方法。

当生产者调用put()方法时,它将元素添加到队列中并调用notify()方法。notify()方法将通知正在等待的线程,即消费者线程,某些条件已经满足。当一个消费者调用take()方法时,它将在队列为空时一直等待。在等待期间,消费者线程调用wait()方法进入等待状态。当一个生产者调用put()方法并添加元素时,它将调用notify()方法,这将通知消费者线程,队列中有新元素可用。

同步和互斥

上面的示例中,我们使用了synchronized方法来实现同步和互斥。实际上,Java提供了多种不同的同步和互斥机制。

synchronized方法和synchronized块:这些机制可以保证同一时间只有一个线程可以在临界区域中执行。当然,它们比较简单,但也比较局限。

ReentrantLock:这是Java5中引入的一个新特性。它提供了更灵活的方式来管理锁。它允许线程获取锁并尝试多次获取锁。它还提供了一些高级功能,如超时、可中断锁和公平性设置。

Semaphore:它允许多个线程同时访问某个资源。例如,你可以使用Semaphore管理有限的数据库连接池,这样多个线程可以同时获取数据库连接。

CountDownLatch:它允许一个或多个线程等待一个或多个其他线程完成操作。例如,你可以创建一个CountDownLatch,该CountDownLatch等待一组线程完成它们的操作,然后实现一个主要的启动器线程,该主要的启动器线程等待这些线程的完成。

CyclicBarrier:它允许一组线程等待彼此操作完成。例如,你可以创建一个CyclicBarrier,该CyclicBarrier等待多个线程完成相同的操作,然后实现一个主要的启动器线程,该主要的启动器线程等待这些线程完成其操作。

Java5的并发工具包

随着Java5的到来,Java引入了一个新的并发工具包。这个包提供了一些高级的、线程安全的数据结构和锁,例如:ConcurrentHashMap,BlockingQueue,Semaphore,CountDownLatch等等。通过使用这些新的工具,你可以更容易地实现高性能和线程安全的应用程序。

结论

Java中的线程可以轻松地共享数据和协同工作。我们可以使用wait()和notify()方法来实现线程之间的协同,而synchronized块和synchronized方法可以用来保证同步和互斥。此外,Java5引入了一个新的并发工具包,它提供了一些高级特性来更轻松地实现并发应用程序。