有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程java同步多线程问题

我只是写了一些代码来测试多线程如何同步,但我无法得到预期的结果。代码可以启动3个线程,但只能启动一个线程来处理共享资源。我的代码有什么问题

class ThreadDemo1{
   public static void main (String[] args){
       MultiThread tt = new MultiThread();
       new Thread(tt).start();
       new Thread(tt).start();
       new Thread(tt).start();
   }
}
class MultiThread implements Runnable {
  int tickets = 100;
  Object _lock = new Object();
  public void run () {
    System.out.println(Thread.currentThread().getName());
    synchronized(_lock) {
      while (true) {  
        if (tickets>0) {
          try {
            Thread.sleep(10);
          } catch (Exception e) {}
          System.out.println(Thread.currentThread().getName() + " is selling "+tickets--);
        }
      }
    }
  }
}

共 (2) 个答案

  1. # 1 楼答案

    [1]首先,在您发布的代码中有几个不好的做法/错误:

    (1)Lock对象最好是singleton。您可以使用静态字段对象或类本身(因为内存中只有一个类)

    Object _lock = new Object();
    private static final Object _lock = new Object();
    

    (2)将while(true) {...}从同步块中取出。在您的代码中,如果第一个线程获得锁,它将处理所有票据,并且不会停止。 应该让每个线程在循环的每次迭代中都尝试获得锁

    (3)对于Thread.sleep(10),我猜您的意思是线程正在做一些繁重的工作。但将此类代码放在同步块(或另一个名称:critical region)中并不是一个好的做法。因为一次只有一个线程可以访问同步块。代码的行为类似于单线程程序,因为其他线程必须等待当前运行的线程完成其任务

    请参见以下代码:

    public class ThreadDemo1 {
        public static void main(String[] args) {
            MultiThread tt = new MultiThread();
            new Thread(tt).start();
            new Thread(tt).start();
            new Thread(tt).start();
        }
    }
    
    public class MultiThread implements Runnable {
        private static int tickets = 100;
        private static final Object _lock = new Object();
    
        public void run() {
            System.out.println(Thread.currentThread().getName());
            while (tickets > 0) {
                try {
                    synchronized (_lock) {
                        if (tickets > 0) {
                            System.out.println(Thread.currentThread().getName() + " is selling " + tickets );
                        }
                    }
                    Thread.sleep(10);
                } catch (Exception e) {
                }
            }
        }
    }
    

    [2]其次,如果您只想在拾取票据时同步线程。尝试使用Atomic*类而不是同步块,它没有锁,将为您带来更好的性能。例如:

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class MultiThreadAtomic implements Runnable {
        private static AtomicInteger tickets = new AtomicInteger(100);
    
        public void run() {
            System.out.println(Thread.currentThread().getName());
            int ticketsRemaining = 0;
            while ((ticketsRemaining = tickets.getAndDecrement()) > 0) {
                System.out.println(Thread.currentThread().getName() + " is selling " + ticketsRemaining);
                try {
                    Thread.sleep(10);
                }
                catch(InterruptedException ie) {}
            }
        }
    }
    
  2. # 2 楼答案

    你拿着锁睡觉。如果要这样做,就没有理由使用多线程

    public void run () {
        System.out.println(Thread.currentThread().getName());
        while(tickets > 0) {
            synchronized(_lock) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + " is selling " + tickets );
                }
            }
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
    }
    

    我猜sleep是您处理的占位符。如果可能,您应该在同步块内执行检查和减量操作,但在同步块外执行冗长的处理

    为了让锁和多线程对您有用,您必须确保synchronized代码占用的时间尽可能少,因为这是一次只能由一个线程运行的代码

    在您的代码中,唯一不能有效实现单线程的是您的第一个System.println


    仅供参考,考虑到这一点,如果您的打印报表准确但可能有误,那么最好是:

    public void run () {
        System.out.println(Thread.currentThread().getName());
        while(tickets > 0) {
            int oldTickets = 0;
            synchronized(_lock) {
                if (tickets > 0) {
                    oldTickets = tickets ;
                }
            }
            if(oldTickets > 0) {
                System.out.println(Thread.currentThread().getName() + " is selling " + oldTickets);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }
    }