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();
}
}
-
最近在网上看到有一道考察多线程的题目,题目有两个版本,一个简单,一个要求多一点,分别如下。
- 编程实现三个线程 ABC,并让它们顺次打印ABC。
- 有三个线程分别打印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 多线程编程,还有很多要学,等学到的时候如果发现还有解法再来完善,如果您发现我漏掉了某种重要的解法,欢迎联系我,^_^。
版权声明:本文是笔者学习笔记之一,旨在总结个人学习过程中的心得体会,现将该笔记发表,希望能够帮助在这方面有疑惑的同学解决一点疑惑,我的目的也就达到了。欢迎分享和转载,转载请注明出处,本文使用Markdown格式编写,为了更好的阅读体验,需要转载的可联系我获取Markdown源文件,谢谢合作。由于笔者水平