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