关于线程通信问题(面试题)

关于volitale,notify,wait面试问题:

1、问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程

      解决思路:上述情况如果不添加volatile关键字就会是一个线程一直循环等待

  

(1)代码示例:

package net.oschina.tkj.mulitcoding.notifykey;

import java.util.ArrayList;
import java.util.List;

/**
 * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题
 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中
 * 
 * 1.如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程
 * 
 * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待
 * 
 * 
 * @author Freedom
 * 
 */
public class NotifyWaitV1 {

	private static volatile List<String> list = new ArrayList<>();

	public void add() {
		list.add("freedom");
	}

	public int size() {
		return list.size();
	}

	// 开启两个线程
	public static void main(String[] args) {

		final NotifyWaitV1 v1 = new NotifyWaitV1();

		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {

				try {
					for (int i = 0; i < 10; i++) {
						v1.add();
						System.out.println("当前线程:"
								+ Thread.currentThread().getName() + "添加了一个元素!");

						Thread.sleep(100);
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}, "t1");

		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {

				while (true) {
					if (v1.size() == 5) {
						System.out.println("当前线程:"
								+ Thread.currentThread().getName()
								+ " list.size==5退出循环,任务执行完成!");
						throw new RuntimeException();
					}
				}
			}
		}, "t2");

		t2.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		t1.start();

	}

} 

(2)结果展示:

①不添加“volatile”关键字



 

②添加volatile关键字



 

2、用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题?

      解决思路: 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁,示例中只有当t1线程执行结束之后,才会执行t2线程内容。

(1)代码示例

package net.oschina.tkj.mulitcoding.notifykey;

import java.util.ArrayList;
import java.util.List;

/**
 * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题
 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中
 * 
 * 1.如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程
 * 
 * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待
 * 
 * 问题2,用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题?
 * 
 * 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁,
 * 示例中只有当t1线程执行结束之后,才会执行t2线程内容
 * 
 * @author Freedom
 * 
 */
public class NotifyWaitV2 {

	private static volatile List<String> list = new ArrayList<>();

	private static final Object lock = new Object();

	public void add() {
		list.add("freedom");
	}

	public int size() {
		return list.size();
	}

	// 开启两个线程
	public static void main(String[] args) {

		final NotifyWaitV2 v1 = new NotifyWaitV2();

		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {

				try {

					synchronized (lock) {

						for (int i = 0; i < 10; i++) {
							v1.add();
							System.out.println("当前线程:"
									+ Thread.currentThread().getName()
									+ "添加了一个元素!");

							Thread.sleep(100);
							if (v1.size() == 5) {
								System.out.println("当前线程:"
										+ Thread.currentThread().getName()
										+ "  发出了notify通知!");
								lock.notify();
							}
						}

					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}, "t1");

		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {

				synchronized (lock) {
					if (v1.size() != 5) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}
					System.out.println("当前线程:"
							+ Thread.currentThread().getName()
							+ " list.size==5退出循环,任务执行完成!");
					throw new RuntimeException();
				}
			}
		}, "t2");

		t2.start();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		t1.start();

	}
}

(2)运行结果:



 

3、问题2通过notify,wait机制解决了循环空等对资源浪费的情况,但是如果对于高并发情况下大量数据,当执行到某个业务逻辑节点时,需要唤醒另外一个线程对当前节点数据处理,使用notify通知,并不解决线程间的实时通信问题(notify不释放线程的锁),所以需要考虑另外一种思路。

解决方案使用:CountDownLatch类对象

(1)代码示例

package net.oschina.tkj.mulitcoding.notifykey;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题
 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中
 * 
 * ###问题1### 如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程
 * 
 * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待
 * 
 * ###问题2### 用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题?
 * 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁,
 * 示例中只有当t1线程执行结束之后,才会执行t2线程内容
 * 
 * ###问题3### notify与wait方法可以解决循环空转浪费资源的问题,但是没法解决,线程间消息的实时更新问题 因此,需要使用
 * 
 * @author Freedom
 * 
 */
public class NotifyWaitV3 {

	private static volatile List<String> list = new ArrayList<>();

	// private static final Object lock = new Object();

	private static final CountDownLatch countDownLatch = new CountDownLatch(1);

	public void add() {
		list.add("freedom");
	}

	public int size() {
		return list.size();
	}

	// 开启两个线程
	public static void main(String[] args) {

		final NotifyWaitV3 v1 = new NotifyWaitV3();

		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {

				try {

					// synchronized (lock) {

					for (int i = 0; i < 10; i++) {
						v1.add();
						System.out.println("当前线程:"
								+ Thread.currentThread().getName() + "添加了一个元素!");

						Thread.sleep(100);
						if (v1.size() == 5) {
							System.out.println("当前线程:"
									+ Thread.currentThread().getName()
									+ "  发出了countDown通知!");
							// lock.notify();
							countDownLatch.countDown();
						}
					}

					// }
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}, "t1");

		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {

				// synchronized (lock) {
				if (v1.size() != 5) {
					try {
						// lock.wait();
						countDownLatch.await();// t2线程等待
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}
				System.out.println("当前线程:" + Thread.currentThread().getName()
						+ " list.size==5退出循环,任务执行完成!");
				throw new RuntimeException();
			}
			// }
		}, "t2");

		t2.start();
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		t1.start();

	}
}

 (2)运行结果:



 

4、总结

(1)volatile,可以解决多个线程共享数据时的可见性,但是没法保证原子性操作;

(2)wait使得线程处于等待状态,会释放线程的锁;

(3)notify唤醒一个线程,但是会持有线程的锁;

(4)sleep使得线程休眠一段时间,会持有线程的锁;

猜你喜欢

转载自1498116590.iteye.com/blog/2415448