欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar

程序员文章站 2022-05-05 15:17:26
...

2019-11-20 

目录

题目:

解题1

解题2:对象锁

解题3:信号量


题目:

LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar

 

解题1


class FooBar {
	private int n;
	private static Object lock = new Object();
	private static volatile Boolean flag = true;

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {

		for (int i = 0; i < n; i++) {
			synchronized (lock){
				if (!flag){
					lock.wait();
				}
				// printBar.run() outputs "bar". Do not change or remove this line.
				printFoo.run();
				flag = false;
				lock.notify();
			}
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {

		for (int i = 0; i < n; i++) {
			synchronized (lock){
				if (flag){
					lock.wait();
				}
				// printBar.run() outputs "bar". Do not change or remove this line.
				printBar.run();
				flag = true;
				lock.notify();
			}
		}
	}

	public static void main(String[] args) {
		FooBar fooBar = new FooBar(2);
		Runnable runnable1 = new Runnable() {
			@Override
			public void run() {
				try {
					fooBar.foo(new Runnable() {
						@Override
						public void run() {
							System.out.print("foo");
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Runnable runnable2 =new Runnable() {
			@Override
			public void run() {
				try {
					fooBar.bar(new Runnable() {
						@Override
						public void run() {
							System.out.print("bar");
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Thread thread1 = new Thread(runnable1);
		thread1.start();
		Thread thread2 = new Thread(runnable2);
		thread2.start();

	}
}
  • 对象同步锁:确保foo和bar方法只会被一个线程执行
  • 线程休眠与唤醒:wait/notify
//对象同步锁用法推荐
synchronized (obj) {
              while (&lt;condition does not hold&gt;)
                  obj.wait();
              ... // Perform action appropriate to condition
          }

LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar

解题2:对象锁

分析: 将printFoo操作和printBar操作分别加锁,来保证两者串行发生,设置一个boolean类型变量fooTurn来指明当前操作的轮次,当其为True时,打印foo,当其为False时,打印bar。每个线程在同步代码块内部,判断当前轮次和自己的操作是否符合,若符合,则执行打印操作,若不符合,则放弃锁。

class FooBar {
    private int n;
    private boolean fooTurn = true;
    private Object lock = new Object();
    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
            synchronized(lock) {
                if (!fooTurn) lock.wait();
                fooTurn = false;
                // printFoo.run() outputs "foo". Do not change or remove this line.
                printFoo.run();
                lock.notifyAll();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
            synchronized(lock) {
                if (fooTurn) lock.wait();
                fooTurn = true;
                // printBar.run() outputs "bar". Do not change or remove this line.
        	    printBar.run();
                lock.notifyAll();
            }
        }
    }
}

作者:copyreadmachine
链接:https://leetcode-cn.com/problems/print-foobar-alternately/solution/java-suo-by-copyreadmachine/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar

 

解题3:信号量

  • 使用信号量,acquire 就是 值+1
    release 就是 值-1
    简单理解就是当值是0,就可以继续执行,不为零就等待变为0后再执行
public class FooBar {
    private int n;
    //here is the full path, or maybe cann't compile in leetcode.
    java.util.concurrent.Semaphore semaphoreFoo=new java.util.concurrent.Semaphore(0);
    java.util.concurrent.Semaphore semaphoreBar=new java.util.concurrent.Semaphore(0);

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            printFoo.run();
            //由于下面阻塞了,所以这里变为0,下面的方法就能继续执行
            semaphoreBar.release();
            //这里让他等一会,等到bar()执行完
            semaphoreFoo.acquire();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // 进来先变为1,就会等上面的release()使他变为0,才进行,所以肯定在foo之后。
            semaphoreBar.acquire();
            printBar.run();
            //bar()执行完了,就让foo()继续。
            semaphoreFoo.release();
        }
    }
}

LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar

  • Java 并发包中的信号量 Semaphore 实际上是一个功能完毕的计数信号量,从概念上讲,它维护了一个许可集
    合,对控制一定资源的消费与回收有着很重要的意义。Semaphore 可以控制某个资源被同时访问的任务数,它
    通过acquire()获取一个许可,release()释放一个许可。如果被同时访问的任务数已满,则其他 acquire 的任务
    进入等待状态,直到有一个任务被 release 掉,它才能得到许可。
  • 就是 Semaphore 仅仅是对资源的并发访问的任务数进行监控,而不会保证线程安全,因此,在访问的时候,要自己控制线程的安全访问。