两个线程交替打印1-100的奇数和偶数

问题定义

启动a、b两个线程,由这两个线程打印出1到100共一百个数字。

要求:

  1. a线程打印1、3、5、7等奇数,b线程打印2、4、6、8等偶数。
  2. 依次串行打印,即打印完1之后,再打印2,然后是3、4、5…直到100,全部打印完成后,进程能正常结束。

解决思路

两个线程交替打印,就是要两个线程之间进行同步,走走停停。线程的同步可以使用Object类自带的wait和notify(或者notifyAll)方法实现。
每个线程都是执行完一次打印之后,唤醒另外一个线程,然后自己进入等待状态。
需要注意的是:
(1)线程的启动顺序,需要先启动一个线程进入等待状态;另一个线程后启动直接执行,执行完唤醒等待线程,自己进入等待。
(2)线程何时退出,等打印了100之后,就不需要再等待了,而是退出。

代码实现

public class PrintNum implements Runnable{
    //是否现在先运行
    private boolean runNow;
    private Object lock;
    private int num;

    public PrintNum(boolean runNow, Object lock, int num) {
        this.runNow = runNow;
        this.lock = lock;
        this.num = num;
    }

    @Override
    public void run(){
        synchronized(lock){
            while(num <= 100) {
                if (runNow) {
                    //第一次进来可以直接运行,但是循环第二次之后需要先等到唤醒才能运行
                    runNow = false;
                } else {
                    //先等待
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println(num);
                num += 2;

                //通知其他线程运行
                lock.notify();
            }
        }
    }

    public static void main(String[] args) {
        Object lock = new Object();
        Thread t1 = new Thread(new PrintNum(true, lock, 1));
        Thread t2 = new Thread(new PrintNum(false, lock, 2));
        //t2先运行,先进入等待状态
        t2.start();
        //t1直接运行,唤醒t2之后进入等待
        t1.start();
    }
}

使用总结

wait()、notify()方法的相同点与区别:

相同点:

  1. 都是Object类提供的native方法。
  2. 都必须要在 同步方法或同步块synchronized中 调用。
  3. 被调用时,当前线程必须拥有该对象的锁。

不同点:

  1. wait()被调用之后,当前线程立即释放锁,等待被唤醒,被唤醒之后,再次获得锁才能继续往下执行
  2. notify()被调用之后,会唤醒等待在该对象上面的一个线程,但是当前线程并不立即释放锁,而是在执行完synchronized块之后才会释放锁。

一句话总结:wait使当前线程停止运行,而notify使停止的线程继续运行。它们俩搭配使用,就可以实现进程的通知、等待(或者叫做同步,或者进程的走走停停)。

使用wait、notify的一般格式:

// 线程1
//锁住对象
synchoronized(lockObj){
	//当条件不满足时,线程等待
	while(!condition) {
		lockObj.wait();
	}
	// 被唤醒之后,且满足了条件,执行某些操作
	doSomething();
}

//线程2
synchronized(lockObj){
	doSomething();
	// 唤醒等待在LockObj对象上面的一个线程;或者使用lockObj.notifyAll()唤醒所有等待在该对象上面的线程
	lockObj.notify();
}

另一种方式

除了使用synchronized配合wait、notify方法实现进程的同步,还可以使用jdk提供的工具类来实现进程的通信。

Lock

Condition await、signal、signalAll

同时这些更加灵活,可以自定义唤醒不同的线程。

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

// thread 1
lock.lock();
condition.await();
doSomething();
lock.unlock();

// thread 2
lock.lock();
doSomething();
condition.signal();
lock.unlock();

利用信号量Semaphore实现

public class PrintNum {
    private int n;
    private Semaphore odd = new Semaphore(1);
    private Semaphore even = new Semaphore(0);

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

    public void printOdd() {
        for (int i=1; i<=n; i+=2) {
            try {
                odd.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
            even.release();
        }

    }

    public void printEven() {
        for(int i=2; i<=n; i+=2) {
            try {
                even.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
            odd.release();
        }
    }

    public static void main(String[] args){
        PrintNum printNum = new PrintNum(10);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> {
            printNum.printOdd();
        });
        executorService.submit(() -> {
            printNum.printEven();
        });
        executorService.shutdown();

    }
}

扩展问题:3个线程交替打印1、2、3;4、5、6…

package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    /**
     * 测试三个线程轮流打印 1、2、3; 4、5、6; 7、8、9;
     *
     */
    private static void testThreeThreadPrint() {
        Lock lock = new ReentrantLock();
        Condition c1 = lock.newCondition();
        Condition c2 = lock.newCondition();
        Condition c3 = lock.newCondition();
        Wrapper wrapper = new Wrapper(1);

        TestThread t1 = new TestThread(lock, c1, c2, wrapper);
        TestThread t2 = new TestThread(lock, c2, c3, wrapper);
        TestThread t3 = new TestThread(lock, c3, c1, wrapper);
        t1.start();
        t2.start();
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        lock.lock();
        c1.signal();
        lock.unlock();

    }
    public static void main(String[] args) {
        testThreeThreadPrint();
    }
}

class TestThread extends Thread{
    Lock lock;
    Condition awaitCondition;
    Condition signalCondition;

    Wrapper wrapper;

    public TestThread(Lock lock, Condition awaitCondition, Condition signalCondition, Wrapper wrapper) {
        this.lock = lock;
        this.awaitCondition = awaitCondition;
        this.signalCondition = signalCondition;
        this.wrapper = wrapper;
    }

    @Override
    public void run() {
        while (wrapper.getNum() < 10) {
            lock.lock();

            try {
                awaitCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (wrapper.getNum() < 10) {
                System.out.println(wrapper.getNum());
                wrapper.setNum(wrapper.getNum() + 1);
            }
            signalCondition.signal();

            lock.unlock();
        }

    }
}

class Wrapper {
    private int num;

    public Wrapper(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

你可能感兴趣的:(java)