1114. 按序打印
我们提供了一个类:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 one() 方法
线程 B 将会调用 two() 方法
线程 C 将会调用 three() 方法
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
解法一:Synchronized锁和控制变量
//控制变量
private int flag = 0;
//定义object 对象为锁
private Object lock = new Object();
public FooMethodTwo()
{
}
public void first (Runnable printFirst) throws InterruptedException
{
synchronized (lock)
{
//如果falg 不为0 则让first 线程等待,while 循环控制first 线程 如果不满足条件
while (flag != 0)
{
lock.wait();
}
printFirst.run();
flag = 1;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException
{
synchronized (lock)
{
//如果成员变量不为1 则让二号等待
while (flag != 1)
{
lock.wait();
}
printSecond.run();
flag = 2;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException
{
synchronized (lock)
{
//如果成员变量不为2 则一直处于等待的状态
while (flag != 2)
{
lock.wait();
}
printThird.run();
flag = 0;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public static void main(String[] args)
{
FooMethodTwo foo = new FooMethodTwo();
Thread thread1 = new Thread();
try
{
foo.first(() ->
{
System.out.println("1");
});
} catch (InterruptedException e)
{
e.printStackTrace();
};
Thread thread2 = new Thread();
try
{
foo.second(() ->
System.out.println("2")
);
} catch (InterruptedException e)
{
e.printStackTrace();
}
Thread thread3 = new Thread();
try
{
foo.third(() -> {
System.out.println("3");
});
} catch (InterruptedException e)
{
e.printStackTrace();
}
thread1.start();
thread2.start();
thread3.start();
}
解法二:CountDownLatch
//声明两个CountDownLatch 变量
private CountDownLatch second;
private CountDownLatch third;
public FooMethodOne() {
//初始化每个CountDownLatch的值为1,表示有一个线程执行完后,执行等待的线程
second = new CountDownLatch(1);
third = new CountDownLatch(1);
}
public void first(Runnable printFirst) throws InterruptedException {
//当前只有first线程没有任何的阻碍,其余两个线程都处于等待阶段
//直到second里面计数为0才执行因调用该second.await()而等待的线程
printFirst.run();
second.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
//只有second 为0 才能通过,否则会一直阻塞
second.await();
printSecond.run();
//直到third 里面计数为0才执行因为调用该 third.await()而等待的线程
third.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
//只有third 为0 才通过 否则一直阻塞
third.await();
printThird.run();
}
public static void main(String[] args)
{
FooMethodOne foo = new FooMethodOne();
Thread thread1 = new Thread();
try
{
foo.first(() ->
{
System.out.println("one");
});
} catch (InterruptedException e)
{
e.printStackTrace();
};
Thread thread2 = new Thread();
try
{
foo.second(() ->
System.out.println("two")
);
} catch (InterruptedException e)
{
e.printStackTrace();
}
Thread thread3 = new Thread();
try
{
foo.third(() -> {
System.out.println("three");
});
} catch (InterruptedException e)
{
e.printStackTrace();
}
thread1.start();
thread2.start();
thread3.start();
}
解法三:Semaphore(信号量)
Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底
获得Semaphore的线程处理完它的逻辑之后,你就可以调用它的Release()函数将它的计数器重新加1,这样其它被阻塞的线程就可以得到调用了
//控制变量
private int flag = 0;
//定义object 对象为锁
private Object lock = new Object();
public FooMethodTwo()
{
}
public void first (Runnable printFirst) throws InterruptedException
{
synchronized (lock)
{
//如果falg 不为0 则让first 线程等待,while 循环控制first 线程 如果不满足条件
while (flag != 0)
{
lock.wait();
}
printFirst.run();
flag = 1;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException
{
synchronized (lock)
{
//如果成员变量不为1 则让二号等待
while (flag != 1)
{
lock.wait();
}
printSecond.run();
flag = 2;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException
{
synchronized (lock)
{
//如果成员变量不为2 则一直处于等待的状态
while (flag != 2)
{
lock.wait();
}
printThird.run();
flag = 0;
//唤醒其余所有的线程
lock.notifyAll();
}
}
public static void main(String[] args)
{
FooMethodTwo foo = new FooMethodTwo();
Thread thread1 = new Thread();
try
{
foo.first(() ->
{
System.out.println("1");
});
} catch (InterruptedException e)
{
e.printStackTrace();
};
Thread thread2 = new Thread();
try
{
foo.second(() ->
System.out.println("2")
);
} catch (InterruptedException e)
{
e.printStackTrace();
}
Thread thread3 = new Thread();
try
{
foo.third(() -> {
System.out.println("3");
});
} catch (InterruptedException e)
{
e.printStackTrace();
}
thread1.start();
thread2.start();
thread3.start();
}
1115. 交替打印FooBar
我们提供一个类:
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 次。
解法一、生产消费者模型
解法二、Semaphore
private int n;
public static void main(String[] args)
{
FooBarMethod01 fb = new FooBarMethod01(6);
//写法一
new Thread(){
@Override
public void run()
{
try
{
fb.foo(() -> System.out.print("foo"));
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}.start();
//写法二
Thread thread2 = new Thread();
thread2.run();
try
{
fb.bar(() -> System.out.print("bar\n"));
} catch (InterruptedException e)
{
e.printStackTrace();
}
thread2.start();
}
public FooBarMethod01(int n)
{
this.n = n;
}
Semaphore foo = new Semaphore(1);
Semaphore bar = new Semaphore(0);
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
foo.acquire();
printFoo.run();
bar.release();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
bar.acquire();
printBar.run();
foo.release();
}
}
知识点:
Java的concurrent包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。
你可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。
CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。
所有材料及题解来源均出于:力扣(LeetCode)
链接:https://leetcode-cn.com/problems
申明:著作权归领扣网络所有,商业转载请联系官方授权。