给你一个类:
public class Foo { public void first() { print("first"); } public void second() { print("second"); } public void third() { print("third"); } }
三个不同的线程 A、B、C 将会共用一个 Foo
实例。
- 线程 A 将会调用
first()
方法 - 线程 B 将会调用
second()
方法 - 线程 C 将会调用
third()
方法
请设计修改程序,以确保 second()
方法在 first()
方法之后被执行,third()
方法在 second()
方法之后被执行。
解答思路:
众所周知,AtomicInteger用于多线程下线程安全的数据读写操作,避免使用锁同步,底层采用CAS实现,内部的存储值使用volatile修饰,因此多线程之间是修改可见的。
public AutomicInteger(int initValue): 有参构造,初始化为initValue
public AutomicInteger(): 无参构造,相当于AutomicInteger(0)
方法:
public int get: 返回当前值
public int getAndIncrement(): 返回当前值,并自增1
public int incrementAndGet(): 先自增1,再返回自增后的值
public boolean compareAndSet(int source, int dest):source是改之前的值,Dest是改之后的值,source与当前真实值匹配了才能执行成功,返回值表示是否执行成功。
public int getAndAdd(int delta): 先获取当前值,再进行计算val = val + delta
public int addAndGet(int delta): 先计算 val = val + delta,再返回最新值。delata可以为负数
private AtomicInteger first=new AtomicInteger(0);
private AtomicInteger second=new AtomicInteger(0);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
first.getAndAdd(1);
}
public void second(Runnable printSecond) throws InterruptedException {
while(first.get()!=1){}
// printSecond.run() outputs "second". Do not change or remove this line.
if(first.get()==1)
printSecond.run();
second.getAndAdd(1);
}
public void third(Runnable printThird) throws InterruptedException {
while(second.get()!=1){}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
给你一个类:
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
实例:
- 线程 A 将会调用
foo()
方法,而 - 线程 B 将会调用
bar()
方法
请设计修改程序,以确保 "foobar"
被输出 n
次。
示例 1:
输入:n = 1 输出:"foobar" 解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
示例 2:
输入:n = 2 输出:"foobarfoobar" 解释:"foobar" 将被输出两次。
class FooBar {
private int n;
private Object obj=new Object();
private volatile boolean fooExec=true;
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized(obj){
if(!fooExec){
obj.wait();
}
printFoo.run();
fooExec=false;
obj.notifyAll();
}
// printFoo.run() outputs "foo". Do not change or remove this line.
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized(obj){
// printBar.run() outputs "bar". Do not change or remove this line.
if(fooExec){
obj.wait();
}
printBar.run();
fooExec=true;
obj.notifyAll();
}
}
}
}
现有函数 printNumber
可以用一个整数参数调用,并输出该整数到控制台。
- 例如,调用
printNumber(7)
将会输出7
到控制台。
给你类 ZeroEvenOdd
的一个实例,该类中有三个函数:zero
、even
和 odd
。ZeroEvenOdd
的相同实例将会传递给三个不同线程:
- 线程 A:调用
zero()
,只输出0
- 线程 B:调用
even()
,只输出偶数 - 线程 C:调用
odd()
,只输出奇数
修改给出的类,以输出序列 "010203040506..."
,其中序列的长度必须为 2n
。
实现 ZeroEvenOdd
类:
ZeroEvenOdd(int n)
用数字n
初始化对象,表示需要输出的数。void zero(printNumber)
调用printNumber
以输出一个 0 。void even(printNumber)
调用printNumber
以输出偶数。void odd(printNumber)
调用printNumber
以输出奇数。
示例 1:
输入:n = 2 输出:"0102" 解释:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 "0102"。
示例 2:
输入:n = 5 输出:"0102030405"
思路:
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理地使用公共资源。
void acquire() :从信号量获取一个许可,如果无可用许可前将一直阻塞等待,
void acquire(int permits) :获取指定数目的许可,如果无可用许可前也将会一直阻塞等待
boolean tryAcquire():从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞
boolean tryAcquire(int permits): 尝试获取指定数目的许可,如果无可用许可直接返回false
boolean tryAcquire(int permits, long timeout, TimeUnit unit):
在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false
void release():释放一个许可,别忘了在finally中使用,注意:多次调用该方法,会使信号量的许可数增加,达到动态扩展的效果,如:初始permits为1,调用了两次release,最大许可会改变为2
int availablePermits(): 获取当前信号量可用的许可
class ZeroEvenOdd {
private int n;
private Semaphore zeroSema=new Semaphore(1);
private Semaphore oddSema=new Semaphore(0);
private Semaphore evenSema=new Semaphore(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++){
zeroSema.acquire();
printNumber.accept(0);
if((i& 1)==1){
oddSema.release();
}else{
evenSema.release();
}
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i++){
if((i& 1)==1){
oddSema.acquire();
printNumber.accept(i);
zeroSema.release();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i++){
if((i& 1)==0){
evenSema.acquire();
printNumber.accept(i);
zeroSema.release();
}
}
}
}
3.多线程分别打印ABC
public static void main(String[] args) {
Object o1 = new Object();
AtomicInteger count = new AtomicInteger(0);
AtomicInteger flag = new AtomicInteger(0);
int n=50; //n为一共打印的次数
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (count.get() < n) {
if (flag.get() == 0) {
System.out.println("A");
flag.set(1);
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (count.get() < n) {
if (flag.get() == 1) {
System.out.println("B");
flag.set(2);
}
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (flag.get() == 2) {
System.out.println("C");
if (count.incrementAndGet() < n) {
flag.set(0);
} else {
break;
}
}
}
}
});
t1.start();
t2.start();
t3.start();
}