1.LeetCode 1114 按序打印
题目:设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
方法1:
用两个boolean变量flag1和flag2控制,当第一个线程结束,flag1=true;
线程2只有当flag1=true时,才能执行;
线程3只有当flag2=true时,才能执行。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class Foo {
public Foo() {
}
Thread t1,t2;
boolean flag1 = false ;
boolean flag2 = false ;
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
flag1 = true ;
}
public void second(Runnable printSecond) throws InterruptedException {
while (!flag1){}
printSecond.run();
flag2 = true ;
}
public void third(Runnable printThird) throws InterruptedException {
while (!flag2){}
printThird.run();
}
}
|
|
 |
方法2:join
获取执行first的线程t1和执行second的线程t2,t1和t2初始值为null;
当获取到t1时,t2会等到t1执行完才继续执行;
同样,t3会等到t2执行完之后,才会执行。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Foo {
Thread t1,t2;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
t1 = Thread.currentThread();
printFirst.run();
}
public void second(Runnable printSecond) throws InterruptedException {
while (t1 == null ){};
t1.join();
t2 = Thread.currentThread();
printSecond.run();
}
public void third(Runnable printThird) throws InterruptedException {
while (t2 == null ){};
t2.join();
printThird.run();
}
}
|
|
 |
方法3:wait/notifyAll
使用整型变量mark控制线程2和线程3的执行顺序,
wait/notifyAll方法需要配合synchronized代码块使用,线程1执行之后,会释放锁。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class Foo {
private int mark = 0 ;
private Object lock = new Object();
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
synchronized (lock){
printFirst.run();
mark = 1 ;
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
synchronized (lock){
while (mark != 1 ){ lock.wait(); }
printSecond.run();
mark = 2 ;
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized (lock){
while (mark != 2 ){ lock.wait(); }
printThird.run();
}
}
}
|
|
 |
方法4:CountDownLatch
用两个CountDownLatch实现,当第一个线程执行完毕,latch1减1为0,线程2就会被激活,当线程2执行完成,latch2减1为0,线程3就会被激活。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import java.util.concurrent.CountDownLatch;
class Foo {
CountDownLatch latch1 = new CountDownLatch( 1 );
CountDownLatch latch2 = new CountDownLatch( 1 );
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
latch1.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
latch1.await();
printSecond.run();
latch2.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
latch2.await();
printThird.run();
}
}
|
|
 |
2.Leetcode 1195 交替打印字符串
题目:编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
- 如果这个数字可以被 3 整除,输出 "fizz"。
- 如果这个数字可以被 5 整除,输出 "buzz"。
- 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。
请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:
- 线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。
- 线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。
- 线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。
- 线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。
方法1:wait/notifyAll
给出一个数n,要遍历从1到n所有的数,这里用mark来表示每次遍历到的数,
4个线程之间是并行关系,
线程A的功能是找到只能被3且不能被5整除的数,否则就等待;
线程B的功能是找到只能被5且不能被3整除的数,否则就等待;
线程C的功能是找到只能被15整除的数,否则就等待;
线程D的功能是找到不能被3或5整除的数,否则就等待;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | class FizzBuzz {
private int n;
private int mark = 1 ;
private Object lock = new Object();
public FizzBuzz( int n) {
this .n = n;
}
public void fizz(Runnable printFizz) throws InterruptedException {
while (mark <= n){
synchronized (lock){
if (mark % 3 != 0 || mark % 5 == 0 ){
lock.wait();
continue ;
} else {
printFizz.run();
mark += 1 ;
lock.notifyAll();
}
}
}
}
public void buzz(Runnable printBuzz) throws InterruptedException {
while (mark <= n){
synchronized (lock){
if (mark % 5 != 0 || mark % 3 == 0 ){
lock.wait();
continue ;
} else {
printBuzz.run();
mark += 1 ;
lock.notifyAll();
}
}
}
}
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (mark <= n){
synchronized (lock){
if (mark % 15 != 0 ){
lock.wait();
continue ;
} else {
printFizzBuzz.run();
mark += 1 ;
lock.notifyAll();
}
}
}
}
public void number(IntConsumer printNumber) throws InterruptedException {
while (mark <= n){
synchronized (lock){
if (mark % 3 == 0 || mark % 5 == 0 ){
lock.wait();
continue ;
} else {
printNumber.accept(mark);
mark += 1 ;
lock.notifyAll();
}
}
}
}
}
|
|
 |
方法2:semaphore
利用4个信号量实现,其中用number线程充当入口(信号量初始为1);
在number线程中先判断数字满足哪一个条件,如果满足其一,就将对应的信号量+1,
使得对应的线程获取到信号量,执行输出语句,然后释放number的信号量。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | class FizzBuzz {
private int n;
private int mark = 0 ;
private final Semaphore fizzSema = new Semaphore( 0 );
private final Semaphore buzzSema = new Semaphore( 0 );
private final Semaphore fizzbuzzSema = new Semaphore( 0 );
private final Semaphore numberSema = new Semaphore( 1 );
public FizzBuzz( int n) {
this .n = n;
}
public void fizz(Runnable printFizz) throws InterruptedException {
while ( true ){
fizzSema.acquire();
if (mark > n){ break ;}
printFizz.run();
numberSema.release();
}
}
public void buzz(Runnable printBuzz) throws InterruptedException {
while ( true ){
buzzSema.acquire();
if (mark > n){ break ;}
printBuzz.run();
numberSema.release();
}
}
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while ( true ){
fizzbuzzSema.acquire();
if (mark > n){ break ;}
printFizzBuzz.run();
numberSema.release();
}
}
public void number(IntConsumer printNumber) throws InterruptedException {
while (mark <= n){
numberSema.acquire();
mark += 1 ;
if (mark > n){ break ;}
if (mark % 3 == 0 && mark % 5 != 0 ){
fizzSema.release();
} else if (mark % 5 == 0 && mark % 3 != 0 ){
buzzSema.release();
} else if (mark % 15 == 0 ){
fizzbuzzSema.release();
} else {
printNumber.accept(mark);
numberSema.release();
}
}
fizzSema.release();
buzzSema.release();
fizzbuzzSema.release();
}
}
|
|
 |
3.Leetcode 1115 交替打印FooBar
题目:我们提供一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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:boolean变量+yield
用boolean变量isPrint控制线程先后,这里不用isPrint,执行会超时。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class FooBar {
private int n;
private boolean isPrint;
public FooBar( int n) {
this .n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
while (isPrint){
Thread.yield();
}
printFoo.run();
isPrint = true ;
}
}
public void bar(Runnable printBar) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
while (!isPrint){
Thread.yield();
}
printBar.run();
isPrint = false ;
}
}
}
|
|
 |
方法2:wait/notify
用布尔型变量isPrint来控制两个线程的调用顺序
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class FooBar {
private int n;
private boolean isPrint = false ;
public FooBar( int n) {
this .n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
synchronized ( this ){
while (isPrint){
this .wait();
}
printFoo.run();
isPrint = true ;
this .notify();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
synchronized ( this ){
while (!isPrint){
this .wait();
}
printBar.run();
isPrint = false ;
this .notify();
}
}
}
}
|
|
 |
方法3:CountDownLatch
用两个CountDownLatch相互限制
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class FooBar {
private int n;
private CountDownLatch countFoo;
private CountDownLatch countBar;
public FooBar( int n) {
this .n = n;
countFoo = new CountDownLatch( 0 );
countBar = new CountDownLatch( 1 );
}
public void foo(Runnable printFoo) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
countFoo.await();
printFoo.run();
countFoo = new CountDownLatch( 1 );
countBar.countDown();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
countBar.await();
printBar.run();
countBar = new CountDownLatch( 1 );
countFoo.countDown();
}
}
}
|
|
|
方法4:semaphore
两个线程互相控制对方的信号量,当一个线程执行完,释放另一个线程的信号量,并且等待自己的信号量。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import java.util.concurrent.Semaphore;
public class FooBar {
private int n;
Semaphore semaphoreFoo= new Semaphore( 0 );
Semaphore semaphoreBar= new 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();
semaphoreBar.release();
semaphoreFoo.acquire();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for ( int i = 0 ; i < n; i++) {
semaphoreBar.acquire();
printBar.run();
semaphoreFoo.release();
}
}
}
|
|
 |
4.Leetcode 1116 打印零与奇偶数
题目:假设有这么一个类:
1 2 3 4 5 6 | class ZeroEvenOdd {
public ZeroEvenOdd( int n) { ... }
public void zero(printNumber) { ... }
public void even(printNumber) { ... }
public void odd(printNumber) { ... }
}
|
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:
- 线程 A 将调用 zero(),它只输出 0 。
- 线程 B 将调用 even(),它只输出偶数。
- 线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ,其中序列的长度必须为 2n。
方法1:semaphore
设置3个semaphore
先执行zeroSema输出0,再判断奇偶数;
如果是奇数就执行evenSema,计算1,3,5,...,n;
如果是偶数就执行oddSema,计算2,4,6,...,n;
每次奇偶数进程执行后,释放zeroSema,重新判断奇偶数。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | package 多线程.leetcode;
import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;
/**
* 功能描述:打印0与奇偶数
*
* @author nxf
* @since 2020-06-10
*/
class ZeroEvenOdd2 {
private int n;
private Semaphore zeroSema = new Semaphore( 1 );
private Semaphore evenSema = new Semaphore( 0 );
private Semaphore oddSema = new Semaphore( 0 );
public ZeroEvenOdd2( int n) {
this .n = n;
}
public void zero(IntConsumer printNumber) throws InterruptedException {
for ( int i= 1 ; i<=n; i++){
zeroSema.acquire();
printNumber.accept( 0 );
if (i % 2 == 0 ){
evenSema.release();
} else {
oddSema.release();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for ( int i= 2 ; i <= n; i+= 2 ){
evenSema.acquire();
printNumber.accept(i);
zeroSema.release();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for ( int i= 1 ; i <= n; i+= 2 ){
oddSema.acquire();
printNumber.accept(i);
zeroSema.release();
}
}
public static void main(String[] args) {
try {
char integer = ( char ) System.in.read();
System.out.println( "传入n: " + integer);
int input = ( char ) integer - ( char ) '0' ;
ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(input);
IntConsumer intConsumer = value -> System.out.println(value);
new Thread( new Runnable() {
@Override
public void run() {
try {
zeroEvenOdd.zero(intConsumer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread( new Runnable() {
@Override
public void run() {
try {
zeroEvenOdd.odd(intConsumer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread( new Runnable() {
@Override
public void run() {
try {
zeroEvenOdd.even(intConsumer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
|
 |
|
|
|
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现