如何正确同步两个线程

时间:2019-01-11 04:12:33

标签: java multithreading synchronization semaphore

这是我在学校经常听到的一个问题,但是直到被要求面试之前,从来没有理由惹麻烦。

提示::使用2个线程依次打印"Thread i: The number is 'j'",其中j = 1:100,而i是线程号。线程1只能打印奇数j,线程2只能打印偶数j。

编辑,必须对j的输出进行排序

这是我的尝试,但我没有继续进行采访。我缺少任何基本的部分吗?有什么优化吗?

import java.util.concurrent.Semaphore;

public class ThreadSynchronization implements Runnable {

  private int start;
  private Semaphore semaphore;

  private ThreadSynchronization(int start, Semaphore semaphore) {
      this.start = start;
      this.semaphore = semaphore;
  }

  public static void main(String[] args) {
      Semaphore semaphore = new Semaphore(1, true);
      semaphore.acquireUninterruptibly();

      start(1, semaphore);
      start(2, semaphore);

      semaphore.release();
  }

  private static void start(int start, Semaphore semaphore) {
      ThreadSynchronization ts = new ThreadSynchronization(start, semaphore);
      Thread thread = new Thread(ts);
      thread.start();
      while (thread.getState() != Thread.State.WAITING) ;
  }

  @Override
  public void run() {
      for (int i = start; i <= 100; i += 2) {
          semaphore.acquireUninterruptibly();
          System.out.println("Thread " + start + ": The number is '" + i + "'");
          semaphore.release();
      }
  }
}

5 个答案:

答案 0 :(得分:1)

一个线程可以继续获取并释放Semaphore,而另一个线程则处于饥饿状态。

您可以使用waitnotify进行此操作,请尝试以下操作:

import java.util.concurrent.atomic.AtomicInteger;

class Odd implements Runnable {

    private AtomicInteger integer;
    private final Object lock;

    public Odd(AtomicInteger integer, Object lock) {
        this.integer = integer;
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            try {
                while (integer.get() <= 100) {
                    while (integer.get() % 2 == 0) {
                        lock.notify();
                        lock.wait();
                    }
                    if (integer.get() <= 100) {
                        System.out.println("Thread " +
                                Thread.currentThread().getName() + ": The number is '" + integer.get() + "'");
                    }
                    integer.getAndIncrement();
                    lock.notify();
                }
            } catch (Exception e) {

            }
        }
    }
}

class Even implements Runnable {

    private AtomicInteger integer;
    private final Object lock;

    public Even(AtomicInteger integer, Object lock) {
        this.integer = integer;
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            try {
                while (integer.get() <= 100) {
                    while (integer.get() % 2 != 0) {
                        lock.notify();
                        lock.wait();
                    }
                    if (integer.get() <= 100) {
                        System.out.println("Thread " +
                                Thread.currentThread().getName() + ": The number is '" + integer.get() + "'");
                    }

                    integer.getAndIncrement();
                    lock.notify();
                }
            } catch (Exception e) {

            }
        }
    }
}

public class ThreadSynchronization {

    public static void main(String[] args) throws Exception{
        Object lock = new Object();
        AtomicInteger integer = new AtomicInteger(1);
        Odd odd = new Odd(integer, lock);
        Even even = new Even(integer, lock);

        Thread thread1 = new Thread(odd, "1");
        Thread thread2 = new Thread(even, "2");

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
    }

}

答案 1 :(得分:1)

使用对象进行仲裁:

public class Switch {
    private boolean expected;

    public Switch(boolean init) {
        expected = init;
    }

    public void waitFor(boolean value) {
        synchronized(this) {
            while (value != expected) {
                try {
                    wait();
                } catch (InterruptedException ex) {
                    // deal with it
                }
            }
            expected = !expected;
            notifyAll();
        }
    }
}

然后:

public class ThreadSynchronization implements Runnable {
    private static Switch arbiter = new Switch(true);

    private int start;

    private ThreadSynchronization(int start) {
        this.start = start;
    }

    public static void main(String[] args) {
        start(1);
        start(2);
    }

    private static void start(int start) {
        ThreadSynchronization ts = new ThreadSynchronization(start);
        Thread thread = new Thread(ts);
        thread.start();
    }

    @Override
    public void run() {
        boolean odd = start%2 != 0;
        for (int i = start; i <= 100; i += 2) {
            arbiter.waitFor(odd);
            System.out.println("Thread " + start + ": The number is '" + i + "'");
        }
    }
}

答案 2 :(得分:1)

您非常接近正确的解决方案,但是该任务需要2个信号灯:

public class ThreadSynchronization implements Runnable {

    private int start;
    private Semaphore semaphore1;
    private Semaphore semaphore2;

    private ThreadSynchronization(int start, Semaphore semaphore1, Semaphore semaphore2) {
        this.start = start;
        this.semaphore1 = semaphore1;
        this.semaphore2 = semaphore2;
    }

    private static void start(int start, Semaphore semaphore1, Semaphore semaphore2) {
        ThreadSynchronization ts = new ThreadSynchronization(start, semaphore1, semaphore2);
        Thread thread = new Thread(ts);
        thread.start();
    }

    @Override
    public void run() {
        for (int i = start; i <= 100; i += 2) {
            semaphore1.acquireUninterruptibly();
            System.out.println("Thread " + start + ": The number is '" + i + "'");
            semaphore2.release();
        }
    }

    public static void main(String[] args) {
        Semaphore semaphore1 = new Semaphore(1);
        Semaphore semaphore2 = new Semaphore(0);

        start(1, semaphore1, semaphore2);
        start(2, semaphore2, semaphore1); // in reverse order
    }
}

答案 3 :(得分:0)

对于这个简单的任务,只需使用AutomicInteger

 public static class CounterTask implements Runnable {

    private final int id;
    private final AtomicInteger counter;
    private final int max;
    private final IntPredicate predicate;

    public CounterTask(int id, AtomicInteger counter, int max, IntPredicate predicate) {
        this.id = id;
        this.counter = counter;
        this.max = max;
        this.predicate = predicate;
    }

    @Override
    public void run() {
        while (counter.get() <= max) {
            if (predicate.test(counter.get())) {
                System.out.format("Thread %d: The number is '%d'\n", id, counter.get());
                counter.incrementAndGet();
            }
        }
    }
}

public static void main(String... args) throws InterruptedException {
    final int max = 100;
    final AtomicInteger counter = new AtomicInteger();
    Thread oddThread = new Thread(new CounterTask(1, counter, max, val -> val % 2 == 0));
    Thread evenThread = new Thread(new CounterTask(2, counter, max, val -> val % 2 != 0));

    oddThread.start();
    evenThread.start();

    oddThread.join();
    evenThread.join();
}

答案 4 :(得分:0)

虽然waitnotify可以胜任,但我认为Semaphore的使用可以使代码更具可读性。下面的代码着重于线程之间相互“交谈”并在需要时进行同步的解决方案:我想象在一个实际用例中,两个线程会做重要的工作,并且在某些时候需要同步并确定谁先行。

import java.util.concurrent.Semaphore;

public class LockStep {

    public static void main(String[] args) {

        Semaphore evenTurn = new Semaphore(1);
        Semaphore oddTurn = new Semaphore(0);

        int max = 50;

        Thread even = new Thread(new Worker(evenTurn, oddTurn, max));
        even.start();
        Thread odd = new Thread(new Worker(oddTurn, evenTurn, max));
        odd.start();

        try {
            even.join();
            odd.join();
        } catch (Exception e) {
            System.out.println("No join for me: " + e);
        }
        System.out.println("Finished");
    }

    static class Worker implements Runnable {

        final Semaphore myTurn;
        final Semaphore theirTurn;
        final int maxTurns;

        public Worker(Semaphore myTurn, Semaphore theirTurn, int maxTurns) {
            this.myTurn = myTurn;
            this.theirTurn = theirTurn;
            this.maxTurns = maxTurns;
        }

        @Override
        public void run() {

            int turn = 0;
            while (turn < maxTurns) {
                try {
                    myTurn.acquire();
                    turn += 1;
                    System.out.println(Thread.currentThread().getName() + " " + turn);
                    theirTurn.release();
                } catch (Exception e) {
                    System.out.println("Oops: " + e);
                }
            }
        }
    }
}