繁体   English   中英

如何正确同步两个线程

[英]How to properly synchronize two threads

这是我在学校一直听到的一个问题,但在我被要求接受采访之前从未有过这样的理由。

提示:使用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();
      }
  }
}

一个线程可以继续获取和释放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();
    }

}

使用对象仲裁:

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个信号量:

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
    }
}

对于这个简单的任务,使用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();
}

虽然waitnotify可以完成这项工作,但我认为使用Semaphore可以使代码更具可读性。 下面的代码集中在一个解决方案,让线程“相互”并在需要的地方同步:我想在一个真实的用例中,2个线程做了重要的工作,并且在某些时候需要同步并确定谁先行。

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);
                }
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM