正解:有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() {


			@Override
			public 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() {


			@Override
			public 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() {


			@Override
			public 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;
		}


		@Override
		public 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;
	}
	@Override
	public 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();

你可能感兴趣的:(Java技术专家)