有三个线程名字分别是A、B、C,每个线程只能打印自己的名字,在屏幕上顺序打印 ABC,打印10次。不准使用线程的sleep()

 

package com.demo.learn;

import java.util.concurrent.Semaphore;

public class ThreadDemo {

	static class ThreadA extends Thread {
		private Semaphore semaphoreCurrent;
		private Semaphore semaphoreNext;

		public ThreadA(String name, Semaphore semaphoreCurrent,
				Semaphore semaphoreNext) {
			super(name);
			this.semaphoreCurrent = semaphoreCurrent;
			this.semaphoreNext = semaphoreNext;
		}

		public void run() {
			for (int i = 0; i < 10; i++) {
				try {
					semaphoreCurrent.acquire();
					System.out.println(this.getName());
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					semaphoreNext.release();
				}
			}
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Semaphore semaphoreA = new Semaphore(1);
		Semaphore semaphoreB = new Semaphore(0);
		Semaphore semaphoreC = new Semaphore(0);
		ThreadA threadA = new ThreadA("A", semaphoreA, semaphoreB);
		ThreadA threadB = new ThreadA("B", semaphoreB, semaphoreC);
		ThreadA threadC = new ThreadA("C", semaphoreC, semaphoreA);
		threadA.start();
		threadB.start();
		threadC.start();
	}
}




public class ABC1 extends Thread {
	private static Object o = new Object();
	private static int count = 0;
	private char ID;
	private int id;
	private int num = 0;

	public ABC1(int id, char ID) {
		this.id = id;
		this.ID = ID;
	}

	public void run() {
		synchronized (o) {
			while (num < 10) {
				if (count % 3 == id) {
					System.out.print(ID);
					++count;
					++num;
					o.notifyAll();
				} else {
					try {
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	public static void main(String[] args) {
		(new ABC1(0, 'A')).start();
		(new ABC1(1, 'B')).start();
		(new ABC1(2, 'C')).start();
	}
}


 

public class ABC2 extends Thread {
	private static String currentThread = "A";
	private static byte[] lock = new byte[0];
	private String name = "";
	private int count = 10;

	public ABC2(String name) {
		this.name = name;
	}

	public void run() {
		while (count > 0) {
			synchronized (lock) {
				if (currentThread.equals(this.name)) {
					System.out.print(name);
					count--;
					if (currentThread.equals("A")) {
						currentThread = "B";
					} else if (currentThread.equals("B")) {
						currentThread = "C";
					} else if (currentThread.equals("C")) {
						currentThread = "A";
						System.out.println();
					}
				}
			}
		}
	}

	public static void main(String[] args) {
		(new ABC2("A")).start();
		(new ABC2("B")).start();
		(new ABC2("C")).start();
	}
}


 


最近在网上看到有一道考察多线程的题目,题目有两个版本,一个简单,一个要求多一点,分别如下。

  1. 编程实现三个线程 ABC,并让它们顺次打印ABC。
  2. 有三个线程分别打印A、B、C,请用多线程编程实现,在屏幕上循环打印 10 次 ABCABC…

只打印一次的做法

1. 使用 join 是最简单的,关于 join 可以参考我的另一篇博文。具体代码如下:

/**
 * 编程实现三个线程ABC,并让它们顺次打印ABC
 * created by ke zhenxu on 3/25/16.
 */
private static void printOnce () {
    final Thread threadA = new Thread () {
        @Override
        public void run () {
            System.out.print ("A");
        }
    };
    final Thread threadB = new Thread () {
        @Override
        public void run () {
            try {
                threadA.join (); // 等待 A 运行完
                System.out.print ("B");
            } catch (Exception e) {
                e.printStackTrace ();
            }
        }
    };
    final Thread threadC = new Thread () {
        @Override
        public void run () {
            try {
                threadB.join (); // 等待 B 运行完
                System.out.print ("C");
            } catch (Exception e) {
                e.printStackTrace ();
            }
        }
    };
    threadA.start ();
    threadB.start ();
    threadC.start ();
}

2. 甚至可以将上面带有注释的 join 方法移到下面的 start 那里去,代码可以更简化成下面这样:

static class MyThread extends Thread {
    public MyThread (final String name) {
        super (name);
    }

    @Override
    public void run () {
        System.out.print (currentThread ().getName ());
    }
}

private static void printOnceV2 () throws InterruptedException {
    final MyThread threadA = new MyThread ("A");
    final MyThread threadB = new MyThread ("B");
    final MyThread threadC = new MyThread ("C");
    threadA.start ();
    threadA.join ();        // 等待 A 运行完,再开始 B
    threadB.start ();
    threadB.join ();        // 等待 B 运行完,再开始 C
    threadC.start ();
}

不过这种方法和串行执行好像没什么两样,不过意思达到了。

3. 使用 JDK5 提供的新 java.util.concurrent (以下简称j.u.c)包中的 CountDownLatch 类,关于 CountDownLatch 的使用可以参考我的另一篇博文,具体代码如下:

private static void printOnceLatch () {
    final CountDownLatch countDownLatchForB = new CountDownLatch (1);   // 为 B 准备的闭锁,只有该闭锁倒数到 0 ,B 才可以运行
    final CountDownLatch countDownLatchForC = new CountDownLatch (1);   // 为 C 准备的闭锁,只有该闭锁倒数到 0 ,C 才可以运行
    final Thread threadA = new Thread () {
        @Override
        public void run () {
            System.out.print ("A");
            // B 要在 A 运行完才能运行,就由 A 来倒数
            countDownLatchForB.countDown ();
        }
    };
    final Thread threadB = new Thread () {
        @Override
        public void run () {
            try {
                // 等待 A (使用给 B 的闭锁)倒数
                countDownLatchForB.await ();
            } catch (Exception e) {
                e.printStackTrace ();
            } finally {
                System.out.print ("B");
                countDownLatchForC.countDown ();
            }
        }
    };
    final Thread threadC = new Thread () {
        @Override
        public void run () {
            try {
                countDownLatchForC.await ();
            } catch (Exception e) {
                e.printStackTrace ();
            } finally {
                System.out.print ("C");
            }
        }
    };
    threadA.start ();
    threadB.start ();
    threadC.start ();
}

打印 10 次的做法,由于可以打印十次的方法略作修改就可以用于只打印一次的方法,因此为了简化,直接给出打印十次的方法。当然,只打印一次的方法略作修改也可以用来打印十次,像下面这样,不过这样有意思吗。

public static void foo() {
    for (int i = 0; i < 10; i++) {
        printOnce();
    }
}

4. 使用信号量 Semaphore 类,三个线程每一个都有一个信号量,信号量等于 0 将被阻塞,等于 1 可以运行,因为每次只能有一个线程在运行,因此信号量总和应该为 1 ,相当于一个令牌在传递一样。代码如下:

private static void printTenTimesSemaphore () {
    final Semaphore semaphoreForA = new Semaphore (1);  // A 最开始执行, 所以是 1
    final Semaphore semaphoreForB = new Semaphore (0);
    final Semaphore semaphoreForC = new Semaphore (0);
    final Thread threadA = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    semaphoreForA.acquire ();
                } catch (Exception e) {
                    e.printStackTrace ();
                } finally {
                    System.out.print ("A");
                    semaphoreForB.release (); // 给 B 的信号加 1 ,让 B 可以获得信号去执行
                }
            }
        }
    };
    final Thread threadB = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    semaphoreForB.acquire ();   // 申请信号, 只有当信号量大于 0 时才不会被阻塞
                } catch (Exception e) {
                    e.printStackTrace ();
                } finally {
                    System.out.print ("B");
                    semaphoreForC.release ();   // 给 C 的信号加 1, 让 C 可以获得信号执行
                }
            }
        }
    };
    final Thread threadC = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    semaphoreForC.acquire ();
                } catch (Exception e) {
                    e.printStackTrace ();
                } finally {
                    System.out.print ("C");
                    semaphoreForA.release ();
                }
            }
        }
    };
    threadA.start ();
    threadB.start ();
    threadC.start ();
}

5. 使用 j.u.c 包中的原子变量,该变量的效果实际和令牌一样,只是根据该变量的状态来判断当前线程是不是可以运行,代码用到了 yield 方法,该方法可以让当前线程主动放弃执行权。代码如下:

/**
 * 编程实现三个线程ABC,并让它们顺次打印 ABC 十次
 */
private static void printTenTimes () {
    final AtomicInteger token = new AtomicInteger (0);
    final Thread threadA = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                while (token.get () % 3 != 0) {
                    yield ();       // 不是 A 运行的时候, 让出执行权
                }
                System.out.print ("A");
                token.incrementAndGet ();  // 由于增加 1 后被 3 模余数 1, 而被 3 模余数 1 是 B 可以运行的, 此步相当于把令牌传递给 B 
            }
        }
    };
    final Thread threadB = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                while (token.get () % 3 != 1) {
                    yield ();
                }
                System.out.print ("B");
                token.incrementAndGet ();
            }
        }
    };
    final Thread threadC = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                while (token.get () % 3 != 2) {
                    yield ();
                }
                System.out.print ("C");
                token.incrementAndGet ();
            }
        }
    };
    threadA.start ();
    threadB.start ();
    threadC.start ();
}

6. 使用 wait 和 notify 方法实现,关于 wait 和 notify 的用法可以参考我的另一篇博文。使用到了 BitSet 类,其中的 mutex 变量作为三个线程的互斥锁,代码如下:

private static void printTenTimesWaitNotif () {
    final BitSet mutex = new BitSet (3);
    final Thread threadA = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    synchronized (mutex) {
                        while (!mutex.get (0)) {
                            mutex.wait ();
                        }
                        System.out.print ("A");
                        mutex.clear (0);
                        mutex.set (1);
                        mutex.notify ();
                    }
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }
        }
    };
    final Thread threadB = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    synchronized (mutex) {
                        while (!mutex.get (1)) {
                            mutex.wait ();
                        }
                        System.out.print ("B");
                        mutex.clear (1);
                        mutex.set (2);
                        mutex.notify ();
                    }
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }
        }
    };
    final Thread threadC = new Thread () {
        @Override
        public void run () {
            for (int i = 0; i < 10; i++) {
                try {
                    synchronized (mutex) {
                        while (!mutex.get (2)) {
                            mutex.wait ();
                        }
                        System.out.print ("C");
                        mutex.clear (2);
                        mutex.set (0);
                        mutex.notify ();
                    }
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }
        }
    };
    threadA.start ();
    threadB.start ();
    threadC.start ();
    synchronized (mutex) {
        mutex.set (0);
        mutex.notify ();
    }
}

总结

关于 Java 多线程编程,还有很多要学,等学到的时候如果发现还有解法再来完善,如果您发现我漏掉了某种重要的解法,欢迎联系我,^_^。


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值