Java基础-多线程实战

1114. 按序打印

给你一个类:

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

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 实例:

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

1116. 打印零与奇偶数

现有函数 printNumber 可以用一个整数参数调用,并输出该整数到控制台。

  • 例如,调用 printNumber(7) 将会输出 7 到控制台。

给你类 ZeroEvenOdd 的一个实例,该类中有三个函数:zeroevenoddZeroEvenOdd 的相同实例将会传递给三个不同线程:

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

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值