【问题标题】:Multithreading with Singleton单例多线程
【发布时间】:2024-09-12 01:50:01
【问题描述】:

我创建了一个 PrintQueue 类来完成与打印相关的工作。我把它设为单例,因为一台网络打印机由许多用户共享,所以应该创建一个实例。

这是我的代码:

abstract class Document {
    protected String name;
    protected String type;
    Document(){}
    Document(String name){
        this.name = name;
    }
    public String name(){
        return this.name;
    }
    abstract public String type();      
}

class TextDocument extends Document {
    TextDocument(String name){
        super(name);
    }
    @Override
    public String type() {
        // TODO Auto-generated method stub
        return "text";
    }       
}

class PdfDocument extends Document {
    PdfDocument(String name){
        super(name);
    }
    @Override
    public String type() {
        // TODO Auto-generated method stub
        return "PDF";
    }   
}

class Node {
    public Document document;
    public Node next;
    Node(Document d){
        document = d;
    }    
}

class PrintQueue {
    public Node root;
    Node cur;
    private static PrintQueue instance;
    private PrintQueue(){}
    public static synchronized PrintQueue getInstance(){
        if(instance == null){
            instance = new PrintQueue();
        }
        return instance;
    }
    public  void push(Document d){
        if(root == null){
            root = new Node(d);
            root.next =null;
        }else{
             cur = root;
             while(cur.next!= null){
                 cur=cur.next;
             }
             Node newNode = new Node(d);
             cur.next = newNode;
             newNode.next = null;
         }

     }
     public Document pop(){
         if(root == null){
             System.out.println("Queue is empty");
             return null;
         }else{
             Node temp = root;
             root=root.next;
             System.out.println(temp.document.name()+"   "+temp.document.type()+" popped out");
             return temp.document;
         }
     }
     public void displayContent(){
         if(root == null){
             System.out.println("no pending task");
         }else{
             cur = root;
             while(cur!=null){
                System.out.println(cur.document.name()+"    "+cur.document.type()); 
                cur = cur.next;
            }
        }
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub

    } 
}

public class test {
    public static void main(String[] args) {
        Document a= new PdfDocument("loan agreement");
        Document b= new TextDocument("Air Ticket");
        Document c= new PdfDocument("movie ticket");
        Document d= new TextDocument("bike riding");
        PrintQueue p = PrintQueue.getInstance();
        PrintQueue q = PrintQueue.getInstance();
        p.push(a);
        p.push(b);
        q.push(c);
        q.push(d);
        p.displayContent();
        System.out.println("-----------------------------------");
        p.pop();
        q.pop();
        System.out.println("-----------------------------------");
        p.displayContent();
    }    
}

我想在我的代码中实现多线程,push() 方法应该同步。否则,如果一个文档被多个用户发送到打印机,它将不会按预期保存在打印队列中。

我是多线程新手,所以我想我应该将Thread 类扩展为我的printQueue,并在run() 方法中调用push()。但是,我无法以这种方式向push() 发送参数,因为我的printQueue 是单例的。我无法在printQueue 的构造函数中初始化document 以传递给push()

我怎样才能做到这一点?

【问题讨论】:

    标签: java multithreading oop singleton


    【解决方案1】:

    据我所知

    • 您正在处理消费者和生产者问题,适合使用BlockingQueue(示例更新自doc)来保持线程安全:

      class DocProducer implements Runnable {
          private final BlockingQueue queue;
          Producer(BlockingQueue q) { queue = q; }
          public void run() {
              try {
                  while (true) { queue.put(produce()); }
              } catch (InterruptedException ex) { ... handle ...}
          }
          Document produce() { ... }
      }
      
      class DocConsumer implements Runnable {
          private final BlockingQueue queue;
          Consumer(BlockingQueue q) { queue = q; }
          public void run() {
              try {
                  while (true) { consume(queue.take()); }
              } catch (InterruptedException ex) { ... handle ...}
          }
          void consume(Document x) { ... }
      }
      
      class Setup {
          void main() {
              // or use your own thread-safe queue implementation,
              // which is harder to be right, though
              BlockingQueue printQueue = new LinkedBlockingQueue();
      
              DocProducer p = new DocProducer(printQueue);
              DocConsumer c1 = new DocConsumer(printQueue);
              DocConsumer c2 = new DocConsumer(printQueue);
              new Thread(p).start();
              new Thread(c1).start();
              new Thread(c2).start();
          }
      }
      
    • 使用runnalbe而不是扩展线程,或者更好地使用ExecutorService

    • 你希望你的消费者和生产者是多线程的,应该有多线程,但不是你的PrintQueue

    【讨论】:

      【解决方案2】:

      您的问题是您想避免多个线程同时调用PrintQueuepush() 方法,然后只使用该方法synchronized -

           public synchronized void push(Document d){
               if(root == null){
                   root = new Node(d);
                   root.next =null;
               }else{
                   cur = root;
                   while(cur.next!= null){
                       cur=cur.next;
                   }
                   Node newNode = new Node(d);
                   cur.next = newNode;
                   newNode.next = null;
               }
      
           }
      

      通过使此方法synchronized,您确保一次只有一个线程可以进入此方法,因此将将此方法设置为thread-safe

      【讨论】: