我正在研究Java中一个更高级的主题,即多线程主题。
我看到很多代码使用单独的对象锁Object lock = new Object();
来同步某些类数据成员。
package multithreading;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
class ProduceConsume {
private LinkedList<Integer> queue = new LinkedList<>();
private final int LIMIT = 10;
private final Object lock = new Object();
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (lock) {
while (queue.size() == LIMIT) {
lock.wait();
}
queue.add(value++);
lock.notify();
}
}
}
public void consume() throws InterruptedException {
while (true) {
Thread.sleep(1000);
synchronized (lock) {
while (queue.size() == 0) {
lock.wait();
}
System.out.print("Size is: " + queue.size());
int value = queue.removeFirst();
System.out.println("; value is: " + value);
lock.notify();
}
}
}
}
public class ProducerConsumerWaitNotify {
public static void main(String[] args) throws InterruptedException {
ProduceConsume object = new ProduceConsume();
ExecutorService execuor = Executors.newFixedThreadPool(2);
execuor.submit(new Runnable() {
@Override
public void run() {
try {
object.produce();
} catch (InterruptedException ex) {
Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
execuor.submit(new Runnable() {
@Override
public void run() {
try {
object.consume();
} catch (InterruptedException ex) {
Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
execuor.shutdown();
execuor.awaitTermination(1000, TimeUnit.DAYS);
}
}
为什么我们不应该锁定LinkedList对象本身?这不是我看到使用这种技术的唯一例子。这是一个好习惯吗?
但是我觉得如果我有两个单独的类用于生成和使用它并且它将链接列表作为它们的构造函数的成员,我必须在这个链表对象上同步,我是对的吗?
我知道concurrent
包中的类是线程安全的,但这不是我的问题,我问的是上述两种方法之间的最佳实践?
答案 0 :(得分:1)
你可以,因为它是你班级的私人成员而且你只有你可以锁定它。如果管理得好,那么用户只需使用您的类的实例就不会导致死锁。如果该字段是公共的,那么用户可以锁定该字段,如果您在类中使用相同的字段进行同步,则可能会出现死锁。这就是我们为什么不把锁放在这个指针上的原因。
但是,使用单个对象进行锁定有几个原因:
您可以将对象命名为controlLockerObject(用于序列化公共访问),listLockerObject(用于序列化对列表的访问),updateLockerObject(用于序列化用于更新内容的代码区域的访问)等。
您可以将对象声明为final,这样您就无法意外地替换或删除用于同步的对象。
答案 1 :(得分:1)
为什么我们不应该锁定LinkedList对象本身?
您甚至可以锁定LinkedList对象并实现相同的同步优势。
但是我觉得如果我有两个单独的类来生产和 使用链接列表作为构造函数的成员,我 必须在这个链表对象上同步,我是对的吗?
我不会说您必须在链接列表上进行同步,因为这不是唯一的选择。我们可以让生产者和消费者线程同步任何其他singleton
对象或同一/不同类别的其他static
字段,或者像synchronize(Object.class)/synchronize(String.class) etc
一样简单。
我正在询问上述两种方式之间的最佳做法?
关于使用private lock
(您正在使用的那个)的利弊,您可能需要查看Java monitor pattern section of Concurrency In Practice,这是Java并发的最佳资源。
答案 2 :(得分:1)
拥有一个明确的锁定对象(使其成为最终)很好,并且在关注点分离方面很好。它也充当文档,因为人们可以立即看到“哦,他们在这里做了一些锁定,也许我也应该”,这可以防止该代码的未来开发人员意外绕过同步。
答案 3 :(得分:0)
public class ProduceConsumerTest {
private final int poolsize = 10;
ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<Integer>(poolsize);
public static void main(String[] args) {
// TODO Auto-generated method stub
ProduceConsumerTest produceConsumerTest = new ProduceConsumerTest();
Producer producer = produceConsumerTest.new Producer();
Consumer consumer = produceConsumerTest.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run(){
Consume();
}
public void Consume(){
while(true){
synchronized (arrayBlockingQueue) {
while(arrayBlockingQueue.size() == 0){
try {
arrayBlockingQueue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
arrayBlockingQueue.notify();
}
}
arrayBlockingQueue.poll();
arrayBlockingQueue.notify();
System.out.println("Consuming:Current size:"+arrayBlockingQueue.size());
}
}
}
}
class Producer extends Thread{
@Override
public void run(){
produce();
}
private void produce(){
while(true){
synchronized (arrayBlockingQueue) {
while(arrayBlockingQueue.size() == poolsize){
try {
arrayBlockingQueue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
arrayBlockingQueue.notify();
}
}
arrayBlockingQueue.offer(1);
arrayBlockingQueue.notify();
System.out.println("Producing, Current Size:"+arrayBlockingQueue.size());
}
}
}
}
}
您可以锁定对象queue