Leetcode_多线程

JUC

1、按序打印(1114、Easy)

1)题目要求

我们提供了一个类:

public class Foo {
public void first() { print(“first”); }
public void second() { print(“second”); }
public void third() { print(“third”); }
}
三个不同的线程将会共用一个 Foo 实例。

线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。

示例 1:

输入: [1,2,3]
输出: “firstsecondthird”
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。
正确的输出是 “firstsecondthird”。
示例 2:

输入: [1,3,2]
输出: “firstsecondthird”
解释:
输入 [1,3,2] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 third() 方法,线程 C 将会调用 second() 方法。
正确的输出是 “firstsecondthird”。

提示:

尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。

2)我的解法

class Foo {
    private Lock lock=new ReentrantLock();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();
    private Condition c3=lock.newCondition();
    
    private int cur=1;

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        // printFirst.run() outputs "first". Do not change or remove this line.
        
        lock.lock();
        if(cur!=1)c1.await();
        
        printFirst.run();

        cur=2;
        c2.signal();

        lock.unlock();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        // printSecond.run() outputs "second". Do not change or remove this line.

        lock.lock();
        if(cur!=2)c2.await();
        printSecond.run();

        cur=3;
        c3.signal();

        lock.unlock();
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        // printThird.run() outputs "third". Do not change or remove this line.

        lock.lock();
        if(cur!=3)c3.await();

        printThird.run();

        cur=1;
        c1.signal();

        lock.unlock();
    }
}

3)其他解法

1、ReentrantLock

class Foo {
    private int num;
    private ReentrantLock lock;
    private Condition c1;
    private Condition c2;
    private Condition c3;
    public Foo() {
        this.num = 1;
        this.lock = new ReentrantLock();
        this.c1 = lock.newCondition();
        this.c2 = lock.newCondition();
        this.c3 = lock.newCondition();
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        lock.lock();
        try{
            while(num!=1){
                c1.await();
            }
            printFirst.run();
            num = 2;
            c2.signal();
        }finally {
            lock.unlock();
        }
        
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        // printSecond.run() outputs "second". Do not change or remove this line.
        lock.lock();
        try{
            while(num!=2){
                c2.await();
            }
            printSecond.run();
            num = 3;
            c3.signal();
        }finally {
            lock.unlock();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        // printThird.run() outputs "third". Do not change or remove this line.
        lock.lock();
        try{
            while(num!=3){
                c3.await();
            }
            printThird.run();
        }finally {
            lock.unlock();
        }
    }
}

作者:ppppjqute
链接: link
来源:力扣(LeetCode)

2、synchronized

class Foo {
    
    private boolean firstFinished;
    private boolean secondFinished;
    private Object lock = new Object();

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        synchronized (lock) {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            firstFinished = true;
            lock.notifyAll(); 
        }
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        synchronized (lock) {
            while (!firstFinished) {
                lock.wait();
            }
        
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            secondFinished = true;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        synchronized (lock) {
           while (!secondFinished) {
                lock.wait();
            }

            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        } 
    }
}

作者:pulsaryu
链接:link
来源:力扣(LeetCode)

3、volatile

class Foo {

   private volatile boolean firstJobDone = false;
   private volatile boolean secondJobDone = false;

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        firstJobDone=true;
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
         while (!firstJobDone){
            //等待printFirst完成
        }
        
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        
        secondJobDone=true;
    }

    public void third(Runnable printThird) throws InterruptedException {
        while (!secondJobDone){
            //等待printSecond完成
        }
        
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
}

作者:byte-b
链接: link
来源:力扣(LeetCode)

4)自己的优化代码

class Foo {
    
    private volatile int cur=1;

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        // printFirst.run() outputs "first". Do not change or remove this line.
        
        while(cur!=1){}
        
        printFirst.run();
        cur=2;
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        // printSecond.run() outputs "second". Do not change or remove this line.

        while(cur!=2){}
        
        printSecond.run();
        cur=3;
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        // printThird.run() outputs "third". Do not change or remove this line.

        while(cur!=3){}
        
        printThird.run();
        cur=1;
    }
}

5)学到的东西

JUC

synchronized

volatile

2、交替打印FooBar(1115、Medium)

1)题目要求

我们提供一个类:

class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print(“foo”);
}
}

public void bar() {
for (int i = 0; i < n; i++) {
print(“bar”);
}
}
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。

请设计修改程序,以确保 “foobar” 被输出 n 次。

示例 1:

输入: n = 1
输出: “foobar”
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,“foobar” 将被输出一次。
示例 2:

输入: n = 2
输出: “foobarfoobar”
解释: “foobar” 将被输出两次。

2)我的解法

1、

class FooBar {
    private int n;
    private volatile boolean IsFoo; 
    public FooBar(int n) {
        this.n = n;
        IsFoo=true;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            while(!IsFoo){Thread.yield();}
        	// printFoo.run() outputs "foo". Do not change or remove this line.
        	printFoo.run();
            IsFoo=false;
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
             while(IsFoo){Thread.yield();}
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            IsFoo=true;
        }
    }
}

2、

class FooBar {
    private int n;
    private Lock lock=new ReentrantLock();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();
    private boolean IsFoo; 
    public FooBar(int n) {
        this.n = n;
        IsFoo=true;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            lock.lock();
            if(!IsFoo)c1.await();
        	// printFoo.run() outputs "foo". Do not change or remove this line.
        	printFoo.run();
            IsFoo=false;
            c2.signal();
            lock.unlock();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            lock.lock();
             if(IsFoo)c2.await();
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            IsFoo=true;
            c1.signal();
            lock.unlock();
        }
    }
}

3)其他解法

1、

class FooBar {
    private ReentrantLock lock = new ReentrantLock();
    private Condition fooCondition = lock.newCondition();
    private Condition barCondition = lock.newCondition();
    private int count = 1;
    private int n;

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

    public void foo(Runnable printFoo) throws InterruptedException {        
        for (int i = 0; i < n; i++) {
            lock.lock();
            if(count != 1) {
                fooCondition.await();
            }
            // printFoo.run() outputs "foo". Do not change or remove this line.
        	printFoo.run();
            barCondition.signal();
            count=2;
            lock.unlock();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            lock.lock();
            if(count != 2) {
                barCondition.await();
            }
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            fooCondition.signal();
            count=1;
            lock.unlock(); 
        }
    }
}

作者:cr6549
链接: link
来源:力扣(LeetCode)

2、
一、分析题意:
1、(问题一)首先是两条线程异步调用,两条线程不能确定他们谁先谁有,也就是有序性
2、(问题二)在线程执行的时候需要保证foo每次循环都在bar前,这样的话肯定是需要线程不能往下执行。
3、 解决以上两个问题基本就解决了,所以联想到以下两个关键字解决,volatile 可见性且有序,yield()让线程暂停。
二、首先解释两个用到的关键字 volatile yield();
yield :暂停当前正在执行的线程对象,yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执 行状态后马上又被执行。
volatile:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 (实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
三、解题过程
循环执行两次,首先确认目前的flag的状态以确保一定是foo执行(为了解决问题一,关键词volatile),来判断是否往下执 行,如果不是那么让当前不往下执行(关键词yield())
优化:可以在 yield() 时增限制,避免无限的循环。

class FooBar {
    
    private int n;
    private volatile int flag = 0;
	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
         for (int i = 0; i < n; i++) {
			while(flag != 0){
				Thread.yield();
			}
			printFoo.run();
			flag = 1;
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
			while(flag != 1){
				Thread.yield();
			}
			printBar.run();
			flag = 0;
		}
	}
    
}

作者:hellowzqk
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

1、

class FooBar {
    private int n;
    private Lock lock=new ReentrantLock();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();
    private boolean IsFoo; 
    public FooBar(int n) {
        this.n = n;
        IsFoo=true;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            lock.lock();
            if(!IsFoo)c1.await();
        	// printFoo.run() outputs "foo". Do not change or remove this line.
        	printFoo.run();
            IsFoo=false;
            c2.signal();
            lock.unlock();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            lock.lock();
             if(IsFoo)c2.await();
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            IsFoo=true;
            c1.signal();
            lock.unlock();
        }
    }
}

2、

class FooBar {
    private int n;
    private volatile boolean IsFoo; 
    public FooBar(int n) {
        this.n = n;
        IsFoo=true;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            while(!IsFoo){Thread.yield();}
        	// printFoo.run() outputs "foo". Do not change or remove this line.
        	printFoo.run();
            IsFoo=false;
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
             while(IsFoo){Thread.yield();}
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            IsFoo=true;
        }
    }
}

5)学到的东西

ReentrantLock、Condition

volatile ,需要thread.yield();

3、打印零与奇偶数(1116、Medium )

1)题目要求

假设有这么一个类:

class ZeroEvenOdd {
public ZeroEvenOdd(int n) { … } // 构造函数
public void zero(printNumber) { … } // 仅打印出 0
public void even(printNumber) { … } // 仅打印出 偶数
public void odd(printNumber) { … } // 仅打印出 奇数
}
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:

线程 A 将调用 zero(),它只输出 0 。
线程 B 将调用 even(),它只输出偶数。
线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506… ,其中序列的长度必须为 2n。

示例 1:

输入:n = 2
输出:“0102”
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 “0102”。
示例 2:

输入:n = 5
输出:“0102030405”

2)我的解法

class ZeroEvenOdd {
    private int n;
    private Lock lock=new ReentrantLock();
    private Condition c0=lock.newCondition();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=1;i<=n;i++){

            lock.lock();

            while(cur!=0)c0.await();

            printNumber.accept(0);

            if(i%2!=0){cur=1;c1.signal();}
            else{cur=2;c2.signal();}

            lock.unlock();
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2;i<=n;i+=2){
            lock.lock();

            while(cur!=2)c2.await();

            printNumber.accept(i);

            cur=0;
            c0.signal();

            lock.unlock();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1;i<=n;i+=2){

            lock.lock();

            while(cur!=1)c1.await();

            printNumber.accept(i);

            cur=0;
            c0.signal();

            lock.unlock();
        }
    }
}

3)其他解法

1、

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

作者:KevinBauer
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class ZeroEvenOdd {
    private int n;
    private Lock lock=new ReentrantLock();
    private Condition c0=lock.newCondition();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=1;i<=n;i++){

            lock.lock();

            while(cur!=0)c0.await();

            printNumber.accept(0);

            if(i%2!=0){cur=1;c1.signal();}
            else{cur=2;c2.signal();}

            lock.unlock();
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2;i<=n;i+=2){
            lock.lock();

            while(cur!=2)c2.await();

            printNumber.accept(i);

            cur=0;
            c0.signal();

            lock.unlock();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1;i<=n;i+=2){

            lock.lock();

            while(cur!=1)c1.await();

            printNumber.accept(i);

            cur=0;
            c0.signal();

            lock.unlock();
        }
    }
}

5)学到的东西

同上

4、 哲学家进餐(1226、Medium)

1)题目要求

5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)

所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。

假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。

设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。

在这里插入图片描述

哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):

philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。

示例:

输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)

  • a 哲学家编号。
  • b 指定叉子:{1 : 左边, 2 : 右边}.
  • c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
    如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。

提示:

1 <= n <= 60

2)我的解法

1、直接把方法修饰为synchronized,一次只让一个哲学家用餐


class DiningPhilosophers {

    public DiningPhilosophers() {
        
    }

    // call the run() method of any runnable to execute its code
    public synchronized void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        pickLeftFork.run();
        pickRightFork.run();
        eat.run();
        putLeftFork.run();
        putRightFork.run();

    }
}

2、


class DiningPhilosophers {
    private ReentrantLock[] locks=new ReentrantLock[]{
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock()
    };

    private volatile int curNum=0;

    public synchronized void add(){//必须加上synchronized,因为volatile不能保证原子性
        curNum++;
    }

    public synchronized void sub(){//必须加上synchronized,因为volatile不能保证原子性
        curNum--;
    }

    public DiningPhilosophers() {
        
    }

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        int curLeft=-1,curRight=-1;
        if(philosopher==0){curLeft=0;curRight=4;}
        else if(philosopher==1){curLeft=1;curRight=0;}
        else if(philosopher==2){curLeft=2;curRight=1;}
        else if(philosopher==3){curLeft=3;curRight=2;}
        else {curLeft=4;curRight=3;}

        //如果有4个哲学家同时进餐,有死锁的风险,需自旋等待
        while(curNum>=4)Thread.yield();
        add();

        locks[curLeft].lock();
        pickLeftFork.run();

        locks[curRight].lock();
        pickRightFork.run();

        eat.run();

        putLeftFork.run();
        locks[curLeft].unlock();

        putRightFork.run();
        locks[curRight].unlock();

        sub();
    }
}

3)其他解法

1、

class DiningPhilosophers {
    //1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
    private final ReentrantLock[] lockList = {new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock()};

    //限制 最多只有4个哲学家去持有叉子
    private Semaphore eatLimit = new Semaphore(4);

    public DiningPhilosophers() {

    }

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {

        int leftFork = (philosopher + 1) % 5;    //左边的叉子 的编号
        int rightFork = philosopher;    //右边的叉子 的编号

        eatLimit.acquire();    //限制的人数 -1

        lockList[leftFork].lock();    //拿起左边的叉子
        lockList[rightFork].lock();    //拿起右边的叉子

        pickLeftFork.run();    //拿起左边的叉子 的具体执行
        pickRightFork.run();    //拿起右边的叉子 的具体执行

        eat.run();    //吃意大利面 的具体执行

        putLeftFork.run();    //放下左边的叉子 的具体执行
        putRightFork.run();    //放下右边的叉子 的具体执行

        lockList[leftFork].unlock();    //放下左边的叉子
        lockList[rightFork].unlock();    //放下右边的叉子

        eatLimit.release();//限制的人数 +1
    }
}

2、

class DiningPhilosophers {
    //1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
    private final ReentrantLock[] lockList = {new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock(),
            new ReentrantLock()};

    //让 1个哲学家可以 “同时”拿起2个叉子(搞个临界区)
    private ReentrantLock pickBothForks = new ReentrantLock();

    public DiningPhilosophers() {

    }

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {

        int leftFork = (philosopher + 1) % 5;    //左边的叉子 的编号
        int rightFork = philosopher;    //右边的叉子 的编号

        pickBothForks.lock();    //进入临界区

        lockList[leftFork].lock();    //拿起左边的叉子
        lockList[rightFork].lock();    //拿起右边的叉子

        pickLeftFork.run();    //拿起左边的叉子 的具体执行
        pickRightFork.run();    //拿起右边的叉子 的具体执行

        pickBothForks.unlock();    //退出临界区

        eat.run();    //吃意大利面 的具体执行

        putLeftFork.run();    //放下左边的叉子 的具体执行
        putRightFork.run();    //放下右边的叉子 的具体执行

        lockList[leftFork].unlock();    //放下左边的叉子
        lockList[rightFork].unlock();    //放下右边的叉子
    }
}

作者:gfu
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

1、信号量


class DiningPhilosophers {
    private ReentrantLock[] locks=new ReentrantLock[]{
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock()
    };

    private Semaphore limit=new Semaphore(4);//限制四个哲学家同时进餐

    public DiningPhilosophers() {
        
    }

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        int curLeft=-1,curRight=-1;
        if(philosopher==0){curLeft=0;curRight=4;}
        else if(philosopher==1){curLeft=1;curRight=0;}
        else if(philosopher==2){curLeft=2;curRight=1;}
        else if(philosopher==3){curLeft=3;curRight=2;}
        else {curLeft=4;curRight=3;}

        limit.acquire();

        locks[curLeft].lock();
        pickLeftFork.run();

        locks[curRight].lock();
        pickRightFork.run();

        eat.run();

        putLeftFork.run();
        locks[curLeft].unlock();

        putRightFork.run();
        locks[curRight].unlock();

        limit.release();
    }
}

2、Atomic

class DiningPhilosophers {
    private ReentrantLock[] locks=new ReentrantLock[]{
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock(),
        new ReentrantLock()
    };

    private AtomicInteger curNum=new AtomicInteger(0);

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        int curLeft=-1,curRight=-1;
        if(philosopher==0){curLeft=0;curRight=4;}
        else if(philosopher==1){curLeft=1;curRight=0;}
        else if(philosopher==2){curLeft=2;curRight=1;}
        else if(philosopher==3){curLeft=3;curRight=2;}
        else {curLeft=4;curRight=3;}

        //如果有4个哲学家同时进餐,有死锁的风险,需自旋等待
        while(curNum.get()>=4)Thread.yield();
        curNum.getAndIncrement();

        locks[curLeft].lock();
        pickLeftFork.run();

        locks[curRight].lock();
        pickRightFork.run();

        eat.run();

        putLeftFork.run();
        locks[curLeft].unlock();

        putRightFork.run();
        locks[curRight].unlock();

        curNum.getAndDecrement();
    }
}

5)学到的东西

信号量:Semaphore
类似于操作系统中的PV操作,acquire即P操作,release即V操作

Atomic类:Atomic

volatile不能保证原子性,++操作需用synchronized锁起来

信号量锁哲学家(如果锁叉子,需要在每次acquire时判断,因为每次acquire都有可能造成死锁)
ReentrantLock锁叉子

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
unordered_map和map都是C++中的容器,用于存储键值对。然而,它们在多线程环境下的使用有一些区别。 unordered_map是基于哈希表实现的容器,不具有对数据的排序功能。由于哈希表的特性,unordered_map的插入、查找和删除操作的平均时间复杂度都是O(1)。然而,在多线程环境下,对unordered_map进行并发读写操作可能导致数据竞争和错误的结果。因此,如果需要在多线程环境下同时对unordered_map进行读写操作,就需要使用互斥锁或其他同步机制来保证线程安全。 相比之下,map是基于红黑树实现的容器,它对存储的键值对进行排序。map提供了一些线程安全的操作,例如插入和删除操作是安全的,因为它们在内部进行节点的重新平衡。然而,对于并发的读写操作,map并没有内置的线程安全机制。因此,在多线程环境下对map进行并发读写操作时,仍然需要使用互斥锁或其他同步机制来确保线程安全。 综上所述,无论是unordered_map还是map,在多线程环境下都需要采取适当的同步机制来避免竞态条件和数据不一致的问题。通过使用互斥锁或其他同步机制,我们可以实现对这两个容器的安全并发访问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [多线程leetcode-study_resources:学习资源](https://download.csdn.net/download/weixin_38557068/19926672)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [unordered_map多线程崩溃在find](https://blog.csdn.net/guotianqing/article/details/120440508)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值