正解:有3个线程ABC。按照ABC来运行(A线程输出A,B线程输出B,C线程输出C,以此类推,循环输出)。
个人记录:2018年,工作的第6到7个年头。
重点研究自己不太擅长的技术:分布式、高并发、大数据量、数据库优化、高性能、负载均衡等。
刷题是一种态度,是一种好习惯。
前一篇尝试解决ABC按照顺序输出,没能搞定,今天研究了下网友的代码、思路提示,成功搞定。
题目:有3个线程ABC。按照ABC来运行(A线程输出A,B线程输出B,C线程输出C,以此类推,循环输出)。
实现方法1:
有个网友给了点提示:用 Reentrantlock,用它的newCondition() 方法创建3个condition,按顺序调用 condition 的await和signal 方法就可以了,
就不贴代码了,具体看Reentrantlock 和Condition 讲解。
参考了JDK1.6中文API文档,找到了1个例子。
package cn.fansunion.threadabc;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;//有3个线程ABC。按照ABC来运行(A线程输出A,B线程输出B,C线程输出C,以此类推,循环输出)。
//参考JDK官方文档,ReentrantLock和Condition
public class ThreadABCByLockCondition {public static void main(String[] args) {//循环次数final int maxNum = 5;ReentrantLock reentrantLock = new ReentrantLock();Condition aCondition = reentrantLock.newCondition();Condition bCondition = reentrantLock.newCondition();Condition cCondition = reentrantLock.newCondition();// 这个地方不能用String currentThread="A";线程内部,要加final,加了final就不能修改引用了final String[] currentThread = { "A" };Thread aThread = new Thread(new Runnable() {@Overridepublic void run() {for (int index = 0; index < maxNum; index++) {reentrantLock.lock();try {while (currentThread[0] != "A") {aCondition.await();}System.out.print("A");currentThread[0] = "B";bCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {reentrantLock.unlock();}}}});Thread bThread = new Thread(new Runnable() {@Overridepublic void run() {for (int index = 0; index < maxNum; index++) {reentrantLock.lock();try {while (currentThread[0] != "B") {bCondition.await();}System.out.print("B");currentThread[0] = "C";cCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {reentrantLock.unlock();}}}});Thread cThread = new Thread(new Runnable() {@Overridepublic void run() {for (int index = 0; index < maxNum; index++) {reentrantLock.lock();try {while (currentThread[0] != "C") {cCondition.await();}System.out.print("C");// 打印1个换行System.out.println();currentThread[0] = "A";aCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {reentrantLock.unlock();}}}});aThread.start();bThread.start();cThread.start();}}
官方例子
public interface ConditionCondition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
class BoundedBuffer {final Lock lock = new ReentrantLock();final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100];int putptr, takeptr, count;public void put(Object x) throws InterruptedException {lock.lock();try {while (count == items.length) notFull.await();items[putptr] = x; if (++putptr == items.length) putptr = 0;++count;notEmpty.signal();} finally {lock.unlock();}}public Object take() throws InterruptedException {lock.lock();try {while (count == 0) notEmpty.await();Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0;--count;notFull.signal();return x;} finally {lock.unlock();}} }
(ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。)
Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。
实现方法2:
参考了网友的代码,做了一点改造,可读性更强一些。
package cn.fansunion.threadabc;import java.util.concurrent.atomic.AtomicInteger;//使用AtimicInteger,原子CAS,切换正在打印的Thread
public class ThreadABCByAtomic {public static void main(String[] args) throws InterruptedException {Thread a = new Thread(new PrintRunnable("A", 1, 2));Thread b = new Thread(new PrintRunnable("B", 2, 3));Thread c = new Thread(new PrintRunnable("C", 3, 1));a.start();b.start();c.start();a.join();b.join();c.join();}static class PrintRunnable implements Runnable {private String str;private static AtomicInteger runFlag = new AtomicInteger(1);private int mySequence;private int next;private PrintRunnable(String str, int mySequence, int next) {this.str = str;this.mySequence = mySequence;this.next = next;}@Overridepublic void run() {//while循环,性能很差while (true) {if (runFlag.get() == mySequence)print();}}public void print() {System.out.print(str);if (next < mySequence)System.out.println();// cas设置下一个执行序号runFlag.compareAndSet(mySequence, next);}}
}
网友源代码:
//A线程的序列mySequence为啥是3
利用Atomic原子操作,实现顺序控制
@Test
public void test2() throws InterruptedException{Thread a = new Thread(new PrintRunnable2(“A”,3,2));Thread b = new Thread(new PrintRunnable2(“B”,2,1));Thread c = new Thread(new PrintRunnable2(“C”,1,3));a.start();b.start();c.start();a.join();b.join();c.join();
}static class PrintRunnable2 implements Runnable{private String str;private static AtomicInteger runFlag = new AtomicInteger(3);private int mySequence;private int next;private PrintRunnable2(String str,int mySequence,int next) {this.str = str;this.mySequence = mySequence;this.next=next;}@Overridepublic void run() {while(true){if(runFlag .get()== mySequence)print();}}public void print(){System.out.print(str);if(next>mySequence)System.out.println();//cas设置下一个执行序号runFlag.compareAndSet(mySequence, next);}
}
总结:使用while循环,一直等待自己的“锁”。
方法1,是用锁+信号+while循环
方法2,用atomicInteger+while循环。
实际工作中,很少有这种奇葩问题。既然是多线程,自然是希望他们并发执行。
如果任务有先后顺序,就可以换成方法去做了。
问题出处:
http://ifeve.com/question/%e6%9c%893%e4%b8%aa%e7%ba%bf%e7%a8%8babc%e3%80%82%e6%8c%89%e7%85%a7abc%e6%9d%a5%e8%bf%90%e8%a1%8c%ef%bc%88a%e7%ba%bf%e7%a8%8b%e8%be%93%e5%87%baa%ef%bc%8cb%e7%ba%bf%e7%a8%8b%e8%be%93%e5%87%bab%ef%bc%8c/
里面有不少参考思路。
思考:
在方法1的实现中,用了while循环,可以用if条件吗?
while (currentThread[0] != "A") {
aCondition.await();
}
官方的Demo例子中,可以用if条件吗?
while (count == items.length)
notFull.await();
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
