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

关于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使得线程休眠一段时间,会持有线程的锁;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值