<多线程>LeetCode题目汇总

1.按序打印

1114.按序打印
在这里插入图片描述
法1:CountDownLatch

用来控制一个或者多个线程等待多个线程。


维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await()
方法而在等待的线程就会被唤醒。

在这里插入图片描述

    CountDownLatch stage1 = new CountDownLatch(1);
    CountDownLatch stage2 = new CountDownLatch(1);
	
    public void first(Runnable printFirst) throws InterruptedException {
        printFirst.run();
        stage1.countDown();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        stage1.await();
    	printSecond.run();
        stage2.countDown();
    }

    public void third(Runnable printThird) throws InterruptedException {
        stage2.await();
        printThird.run();
    }

法2:Semaphore
Semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数。

    Semaphore stage1 = new Semaphore(1);
    Semaphore stage2 = new Semaphore(0);
    Semaphore stage3 = new Semaphore(0);
	
    public void first(Runnable printFirst) throws InterruptedException {
	stage1.acquire();
        printFirst.run();
        stage2.release();
    }

    public void second(Runnable printSecond) throws InterruptedException {
    	stage2.acquire();
    	printSecond.run();
    	stage3.release();
    }

    public void third(Runnable printThird) throws InterruptedException {
    	stage3.acquire();
        printThird.run();
    }

法3:Lock

java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。


相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。

使用 Lock 来获取一个 Condition 对象。

    Lock lock = new ReentrantLock();
    volatile int stage = 1;
    Condition stage2 = lock.newCondition();
    Condition stage3 = lock.newCondition();
	
    public void first(Runnable printFirst) throws InterruptedException {
	lock.lock();
	try {
		printFirst.run();
		stage = 2;
		stage2.signal();
	}finally {
		lock.unlock();
	}
    }

    public void second(Runnable printSecond) throws InterruptedException {
    	lock.lock();
	try {
		while(stage!=2) {
			stage2.await();
		}
		printSecond.run();
		stage = 3;
		stage3.signal();
	}finally {
		lock.unlock();
	}
    }

    public void third(Runnable printThird) throws InterruptedException {
    	lock.lock();
	try {
		while(stage!=3) {
			stage3.await();
		}
		printThird.run();
	}finally {
		lock.unlock();
	}
    }

法4:无锁

    volatile int stage = 1;
	
    public void first(Runnable printFirst) throws InterruptedException {
	printFirst.run();
	stage = 2;
    }

    public void second(Runnable printSecond) throws InterruptedException {
    	while(stage!=2);
	printSecond.run();
	stage = 3;
    }

    public void third(Runnable printThird) throws InterruptedException {
    	while(stage!=3);
	printThird.run();
    }

法5:CyclicBarrier

用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。

CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset()方法可以循环使用,所以它才叫做循环屏障。

CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction在所有线程都到达屏障的时候会执行一次。

在这里插入图片描述

    CyclicBarrier cb1 = new CyclicBarrier(2);
    CyclicBarrier cb2 = new CyclicBarrier(2);
	
    public void first(Runnable printFirst) throws InterruptedException {
	printFirst.run();
	try {
		cb1.await();
	} catch (BrokenBarrierException e) {
		e.printStackTrace();
	}
    }

    public void second(Runnable printSecond) throws InterruptedException{
    	try {
		cb1.await();
		printSecond.run();
		cb2.await();
	} catch (BrokenBarrierException e) {
		e.printStackTrace();
	}    	
    }

    public void third(Runnable printThird) throws InterruptedException{
    	try {
		cb2.await();
	} catch (BrokenBarrierException e) {
		e.printStackTrace();
	}
	printThird.run();
    }

2.交替打印FooBar

1115.交替打印FooBar
在这里插入图片描述

法1:

class FooBar {
    private int n;

    public FooBar(int n) {
        this.n = n;
    }
    Semaphore stage1 = new Semaphore(1);
    Semaphore stage2 = new Semaphore(0);

    public void foo(Runnable printFoo) throws InterruptedException {     
        for (int i = 0; i < n; i++) {
            stage1.acquire();
        	printFoo.run();
            stage2.release();     	
            } 
    }
    
    public void bar(Runnable printBar) throws InterruptedException {       
        for (int i = 0; i < n; i++) {
            stage2.acquire();
            printBar.run();
            stage1.release();
        }
    }
}

法2:公平锁

class FooBar {
    private int n;

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

    Lock lock = new ReentrantLock(true);
    volatile boolean permitFoo = true;

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
            	if(permitFoo) {
            	    printFoo.run();
                    i++;
                    permitFoo = false;
            	} else {
                    Thread.yield();
                }
            }finally {
            	lock.unlock();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
            	if(!permitFoo) {
            	    printBar.run();
            	    i++;
            	    permitFoo = true;
            	} else {
                    Thread.yield();
                }
            }finally {
            	lock.unlock();
            }
        }
    }
}

法3:无锁

class FooBar {
    private int n;

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

    volatile boolean permitFoo = true;

    public void foo(Runnable printFoo) throws InterruptedException {     
        for (int i = 0; i < n; ) {
            if(permitFoo) {
        	printFoo.run();
            	i++;
            	permitFoo = false;
            }
            Thread.sleep(1);
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {       
        for (int i = 0; i < n; ) {
            if(!permitFoo) {
        	printBar.run();
        	i++;
        	permitFoo = true;
            }
            Thread.sleep(1);
        }
    }
}

法4:CyclicBarrier

class FooBar {
    private int n;

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

    CyclicBarrier cb = new CyclicBarrier(2);
    volatile boolean fin = true;

    public void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            while(!fin);
            printFoo.run();
            fin = false;
            try {
		cb.await();
	    } catch (BrokenBarrierException e) {
	    }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            try {
		cb.await();
	    } catch (BrokenBarrierException e) {
	    }
            printBar.run();
            fin = true;
        }
    }
}

3.打印零与奇偶数

1116.打印零与奇偶数
法1:Semaphore

class ZeroEvenOdd {
    private int n;
    
    public ZeroEvenOdd(int n) {
        this.n = n;
    }

    Semaphore z = new Semaphore(1);
    Semaphore e = new Semaphore(0);
    Semaphore o = new Semaphore(0);
	
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=0; i<n;i++) {
        	z.acquire();
        	printNumber.accept(0);
        	if((i&1)==0) {
        		o.release();
        	}else {
        		e.release();
        	}
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2; i<=n; i+=2) {
        	e.acquire();
        	printNumber.accept(i);
        	z.release();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1; i<=n; i+=2) {
        	o.acquire();
        	printNumber.accept(i);
        	z.release();
        }
    }
}

法2:Lock

class ZeroEvenOdd {
    private int n;
    
    public ZeroEvenOdd(int n) {
        this.n = n;
    }

    Lock lock = new ReentrantLock();
    Condition z = lock.newCondition();
    Condition num = lock.newCondition();
    volatile boolean zTurn = true;
    volatile int zIndex = 0;
	
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(;zIndex<n;) {
            lock.lock();
            try {
        	while(!zTurn) {
        		z.await();
        	}
        	printNumber.accept(0);
        	zTurn = false;
        	num.signalAll();
                zIndex++;
            }finally {
        	lock.unlock();
            }
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2; i<=n; i+=2) {
            lock.lock();
            try {
        	while(zTurn || (zIndex&1)==1) {
        		num.await();
        	}
        	printNumber.accept(i);
        	zTurn = true;
        	z.signal();
            }finally {
        	lock.unlock();
            }
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1; i<=n; i+=2) {
            lock.lock();
            try {
        	while(zTurn || (zIndex&1)==0) {
        		num.await();
        	}
        	printNumber.accept(i);
        	zTurn = true;
        	z.signal();
            }finally {
        	lock.unlock();
            }
        }
    }
}

法3:无锁

class ZeroEvenOdd {
    private int n;
    
    public ZeroEvenOdd(int n) {
        this.n = n;
    }

    volatile int stage = 0;
	
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=0;i<n;i++) {
        	while(stage>0) {
                Thread.yield();
            }
    		printNumber.accept(0);
    		if((i&1)==0) {
    			stage = 1;
    		}else {
    			stage = 2;
    		}
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2; i<=n; i+=2) {
        	while(stage!=2) {
                Thread.yield();
            }
    		printNumber.accept(i);
    		stage = 0;
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1; i<=n; i+=2) {
        	while(stage!=1) {
                Thread.yield();
            }
    		printNumber.accept(i);
    		stage = 0;
        }
    }
}

4.H2O生成

1117.H2O生成
在这里插入图片描述
在这里插入图片描述
法1:Semaphore
h每获取一次释放一个o许可,o每次获取两个许可(即2次h后执行一次o)

class H2O {

    public H2O() {

    }
        Semaphore h = new Semaphore(2);
        Semaphore o = new Semaphore(0);  
    public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
	    h.acquire();
        // releaseHydrogen.run() outputs "H". Do not change or remove this line.
        releaseHydrogen.run();
        o.release();
    }

    public void oxygen(Runnable releaseOxygen) throws InterruptedException {
        o.acquire(2);
        // releaseOxygen.run() outputs "O". Do not change or remove this line.
		releaseOxygen.run();
        h.release(2);
    }
}

法2:

class H2O {

    public H2O() {

    }
        Lock lock = new ReentrantLock();
        Condition cd = lock.newCondition();
        volatile int index = 0; 
    public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
        lock.lock();
        try {
            while (index == 2)
                cd.await();
            index++;
            // releaseHydrogen.run() outputs "H". Do not change or remove this line.
            releaseHydrogen.run();
            cd.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void oxygen(Runnable releaseOxygen) throws InterruptedException {
        lock.lock();
        try {
            while (index < 2)
                cd.await();
            // releaseOxygen.run() outputs "O". Do not change or remove this line.
		    releaseOxygen.run();
            index = 0;
            cd.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

5.交替打印字符串

1195.交替打印字符串

法1:信号量

class FizzBuzz {
    private int n;

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

    Semaphore f = new Semaphore(0);
    Semaphore b = new Semaphore(0);
    Semaphore fb = new Semaphore(0);
    Semaphore num = new Semaphore(1);

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        for (int i = 3; i <= n; i += 3) {
            if (i % 5 != 0) {
                f.acquire();
                printFizz.run();
                num.release();
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        for (int i = 5; i <= n; i += 5) {
            if (i % 3 != 0) {
                b.acquire();
                printBuzz.run();
                num.release();
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        for (int i = 15; i <= n; i += 15) {
            fb.acquire();
            printFizzBuzz.run();
            num.release();
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            num.acquire();
            // printNumber.accept(i);
            if (i % 3 == 0 && i % 5 == 0) {
                fb.release();
            } else if (i % 3 == 0 && i % 5 != 0) {
                f.release();
            } else if (i % 3 != 0 && i % 5 == 0) {
                b.release();
            } else {
                printNumber.accept(i);
                num.release();
            }
        }
    }
}

法2:管程模型

class FizzBuzz {
    private int n;

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

    Lock lock = new ReentrantLock();
    Condition cond = lock.newCondition();
    volatile int state = -1;

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        for (int i = 3; i <= n; i += 3) {
            if (i % 5 == 0)
                continue;
            lock.lock();
            try {
                while (state != 3) 
                    cond.await();
                printFizz.run();
                state = -1;
                cond.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        for (int i = 5; i <= n; i += 5) {
            if (i % 3 == 0)
                continue;
            lock.lock();
            try {
                while (state != 5) 
                    cond.await();
                printBuzz.run();
                state = -1;
                cond.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        for (int i = 15; i <= n; i += 15) {
            lock.lock();
            try {
                while (state != 15) 
                    cond.await();
                printFizzBuzz.run();
                state = -1;
                cond.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            lock.lock();
            try {
                while (state != -1)
                    cond.await();
                if (i % 15 == 0) {
                    state = 15;
                } else if (i % 3 == 0) {
                    state = 3;
                } else if (i % 5 == 0) {
                    state = 5;
                } else {
                    printNumber.accept(i);
                    // state = -1;
                }
                cond.signalAll();         
            } finally {
                lock.unlock();
            }
        }
    }
}

当然也可以有多个condition对象,这个就不写了,复制一下别人的:

private ReentrantLock lock = new ReentrantLock();
private Condition fCond = lock.newCondition();
private Condition bCond = lock.newCondition();
private Condition fbCond = lock.newCondition();
private Condition nCond = lock.newCondition();
private volatile Boolean state = false;

/**
 * use lock
 * @param printFizz
 * @throws InterruptedException
 */
// printFizz.run() outputs "fizz".
public void fizz1(Runnable printFizz) throws InterruptedException {
    for (int i = 3; i <= n; i = i + 3) {
        lock.lock();
        try {
            if (i % 5 != 0) {
                while (!state) {
                    fCond.await();
                }
                printFizz.run();
                state = false;
                nCond.signal();
            }
        }finally {
            lock.unlock();
        }
    }
}

// printBuzz.run() outputs "buzz".
public void buzz1(Runnable printBuzz) throws InterruptedException {
    for (int i = 5; i <= n; i = i + 5) {
        lock.lock();
        try {
            if (i % 3 != 0) {
                while (!state) {
                    bCond.await();
                }
                printBuzz.run();
                state = false;
                nCond.signal();
            }
        }finally {
            lock.unlock();
        }
    }
}

// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz1(Runnable printFizzBuzz) throws InterruptedException {
    for (int i = 15; i <= n; i = i + 15) {
        lock.lock();
        try {
            while (!state) {
                fbCond.await();
            }
            printFizzBuzz.run();
            state = false;
            nCond.signal();
        }finally {
            lock.unlock();
        }

    }
}

// printNumber.accept(x) outputs "x", where x is an integer.
public void number1(IntConsumer printNumber) throws InterruptedException {
    for (int i = 1; i <= n; i++) {
        lock.lock();
        try {
            while (state) {
                nCond.await();
            }
            if (i % 3 == 0 && i % 5 == 0) {
                fbCond.signal();
                state = true;
            }else if (i % 3 == 0) {
                fCond.signal();
                state = true;
            }else if (i % 5 == 0) {
                bCond.signal();
                state = true;
            }else {
                printNumber.accept(i);
                nCond.signal();
            }
        }finally {
            lock.unlock();
        }
    }
}

作者:guessmyname
链接:https://leetcode-cn.com/problems/fizz-buzz-multithreaded/solution/javashi-xian-shi-yong-semaphore-huo-zhe-reentrantl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

法3:无锁
我喜欢这种方案,简洁明了。

class FizzBuzz {
    private int n;
    private volatile int state = -1;

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

    public void fizz(Runnable printFizz) throws InterruptedException {
        for (int i = 3; i <= n; i += 3) {   
            if (i % 5 == 0)    
                continue;
            while (state != 3) {
                Thread.yield();
            }
            printFizz.run();
            state = -1;    
        }
    }

    public void buzz(Runnable printBuzz) throws InterruptedException {
        for (int i = 5; i <= n; i += 5) {   
            if (i % 3 == 0)    
                continue;
            while (state != 5) {
                Thread.yield();
            }
            printBuzz.run();
            state = -1;    //控制权交还给number()方法
        }
    }

    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        for (int i = 15; i <= n; i += 15) {   //只输出15的倍数
            while (state != 15) {
                Thread.yield();
            }

            printFizzBuzz.run();
            state = -1;    //控制权交还给number()方法
        }
    }

    public void number(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; ++i) {
            while (state != -1) {
                Thread.yield();
            }
            if (i % 15 == 0)
                state = 15;  
            else if (i % 5 == 0)
                state = 5;
            else if (i % 3 == 0)
                state = 3;
            else 
                printNumber.accept(i);
        }
    }
}

6.哲学家进餐

1226.哲学家进餐
在这里插入图片描述
在这里插入图片描述

  • 信号量模型与管程模型是线程间协作的两大利器,大部分情况下,二者可以互相转换。它们的区别在于,信号量模型可以让多个线程共享临界区,而管程模型是让线程独占临界区;
  • CountDownLatch主要用于一个线程等待多个线程的场景,而CyclicBarrier则用于多个线程之间互相等待的场景。

参考资料:https://zhuanlan.zhihu.com/p/81626432

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值