一、背景
面试的时候,有一个高频的笔试题:
让2个线程轮流打印,a线程是打印ABCDEFGHIJ,b线程是打印1、2、3、4、5、6、7、8、9、10
二、原理
这种类型的面试题,主要是考察object的wait()方法和notify()方法的使用
wait()方法用法:
o.wait()会让正在o对象上活动的当前线程进入等待状态,并且释放之前占用的o对象的锁
notify()方法用法:
o.notify()唤醒正在o对象上等待的线程, 只会通知,不会释放之前占用的o对象的锁
三、用法
2个线程轮流打印,a线程是打印ABCDEFGHIJ,b线程是打印1、2、3、4、5、6、7、8、9、10
package com.iflytek.sda;
import java.util.Arrays;
import java.util.List;
public class ThreadTest {
private static final Object lock = new Object();
private static boolean ifPrint = false;
public static void main(String[] args) {
Thread ta = new Thread(() ->{
// 要打印的list
List<String> list = Arrays.asList("A","B","C","D","E","F","G","H","I","J");
for (int i = 0; i < list.size(); i++){
synchronized (lock) {
// 如果ifPrint的值为true,就会进入等待状态
while (ifPrint) {
try{
lock.wait();
}catch (Exception e){
}
}
// 到了这一步,说明ifPrint的值为false,然后就会打印
System.out.print(list.get(i));
// 修改ifPrint的值为true,这样再次进入该方法时会进入等待状态,不会进行打印
ifPrint = true;
// 唤醒正在lock对象上等待的线程
lock.notify();
}
}
});
Thread tb = new Thread(() ->{
for (int i = 0; i < 10; i++){
synchronized (lock) {
// 如果ifPrint的值为false,就会进入等待状态
while (!ifPrint) {
try{
lock.wait();
}catch (Exception e){
}
}
// 到了这一步,说明ifPrint的值为true,然后就会打印
System.out.print(i+1);
// 修改ifPrint的值为false,这样再次进入该方法时会进入等待状态,不会进行打印
ifPrint = false;
// 唤醒正在lock对象上等待的线程
lock.notify();
}
}
});
ta.start();
tb.start();
}
}
一开始,ifPrint为false,此时,ta线程会进行打印,打印A;tb线程会处于等待状态;
等ta打印完毕,修改ifPrint为true,并且唤醒其它等待线程。
此时,ta线程会处于等待状态;tb线程会进行打印,打印1
等tb打印完毕,修改ifPrint为false,并且唤醒其它等待线程。
此时,ta线程会进行打印,打印B;tb线程会处于等待状态;
....
以此类推:
四、升级
上面是2个线程,可以通过true、false的两种状态来区分,那如果是3个线程呢,4个线程呢,要怎么办?
3个线程,轮流打印,第一个线程只打印A,第二个线程只打印B,第三个线程只打印C
package com.iflytek.sda;
public class ThreadTest2 {
private static Integer flag = 1;
private static final Object lock = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(()->{
for (int i = 0 ; i < 10; i++){
synchronized (lock){
while(flag != 1){
try{
lock.wait();
}catch (Exception e){
}
}
System.out.print("A");
flag = 2;
lock.notifyAll();
}
}
});
Thread threadB = new Thread(()->{
for (int i = 0 ; i < 10; i++){
synchronized (lock){
while(flag != 2){
try{
lock.wait();
}catch (Exception e){
}
}
System.out.print("B");
flag = 3;
lock.notifyAll();
}
}
});
Thread threadC = new Thread(()->{
for (int i = 0 ; i < 10; i++){
synchronized (lock){
while(flag != 3){
try{
lock.wait();
}catch (Exception e){
}
}
System.out.print("C");
flag = 1;
lock.notifyAll();
}
}
});
threadA.start();
threadB.start();
threadC.start();
}
}
这里的代码写在同一个类里面,标志位写在最上面可以共用,实际用法中都不在一个类里面,要怎么办呢?
package com.example.demo.thread;
public class ThreadPrint {
public static Integer flag = 1;
public static void main(String[] args) {
Thread ta = new Thread(new ThreadA());
Thread tb = new Thread(new ThreadB());
ta.start();
tb.start();
}
}
class ThreadA implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 10; i++){
synchronized (ThreadPrint.class){
while (ThreadPrint.flag != 1){
try {
ThreadPrint.class.wait();
}catch (Exception e){
}
}
System.out.print("1");
ThreadPrint.flag = 2;
ThreadPrint.class.notifyAll();
}
}
}
}
class ThreadB implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 10; i++){
synchronized (ThreadPrint.class){
while (ThreadPrint.flag != 2){
try {
ThreadPrint.class.wait();
}catch (Exception e){
}
}
System.out.print("A");
ThreadPrint.flag = 1;
ThreadPrint.class.notifyAll();
}
}
}
}
好几个线程同理,
另外,超过两个线程就需要使用notifyAll,唤醒对象上的所有线程