下面程序会有什么问题
package examination;
import java.util.ArrayList;
import java.util.List;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
* @author xzq
*/
public class Examination01 {
public static void main(String[] args) {
final Box_1 box=new Box_1();
new Thread("线程1"){
@Override
public void run() {
try {
for(int i=1;i<=10;i++){
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("线程2"){
@Override
public void run() {
while(true){
if(box.size()==5){
break;
}
}
System.out.println("线程2执行完毕!");
}
}.start();
}
}
/**
* 这样实现有什么问题?
* @author xzq
*/
class Box_1{
private List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}
线程2不会得到通知,不会结束,
线程1不通知我 我自己获取 也可以结束,加入下面代码
2、解决问题 为对象添加volatile
private volatile List box=new ArrayList<>();
package examination;
import java.util.ArrayList;
import java.util.List;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
* @author xzq
*/
public class Examination02 {
public static void main(String[] args) {
final Box_2 box=new Box_2();
new Thread("线程1"){
@Override
public void run() {
try {
for(int i=1;i<=10;i++){
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
Thread.sleep(1_000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("线程2"){
@Override
public void run() {
try {
while(true){
if(box.size()==5){
Thread.sleep(2_000);
System.out.println("线程2:我看到容器中已经有5个元素啦!!!");
break;
}
}
System.out.println("线程2:执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
/**
* 加上volatile使box的修改可以得到通知
* 这样实现有什么问题?
*
* 1、由于add和size没有加锁,那么就不具备互斥性,
* 有可能会出现当线程2判断完size==5后,线程切换了
* 然后线程1又加了一次已经到6了,然后在切换回线程2
* 此时,线程2才break,这样就与原逻辑不符。
*
* 2、用死循环的方式十分浪费CPU的资源
* @author xzq
*/
class Box_2{
private volatile List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}
3、使用wait和notify方法
wait()会释放锁,而nodify()不会释放锁。
线程2wait()后释放了锁 线程1进入,但线程1执行到size=5时nodify()了,但线程2并没有继续执行而是等线程1结束才开始执行 说明nodify()不会释放锁
package examination;
import java.util.ArrayList;
import java.util.List;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
*
* 这里使用wait()和notify()方法实现,
* 注意:wait()会释放锁,而nodify()不会释放锁。
*
* 该例中线程2必须比线程1先启动,才能保持监听状态
* 因为必须先wait,才有nodify。
*
* 但是本程序的输出还是没有达到预期效果:
* size=5的时候线程2没有继续往下执行,而是
* 等到线程1执行完毕后才往下执行的,因为
* nodify是不释放锁的,而wait被nodify后是需要
* 重新持有锁的,但是现在这把锁被线程1持有着
* 所以只能等到线程1离开同步代码块时,线程2
* 才能继续往下执行。
* @author xzq
*/
public class Examination03 {
public static void main(String[] args) {
final Box_3 box=new Box_3();
final Object obj=new Object();
new Thread("线程2"){
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("线程2:开始执行");
//4秒后线程1才打印 说明4秒后线程2中wait()释放了锁
Thread.sleep(4000);
if(box.size()!=5){
obj.wait();
}
System.out.println("线程2:执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread("线程1"){
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("线程1:开始执行");
for(int i=1;i<=10;i++){
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
if(box.size()==5){
obj.notify();
}
Thread.sleep(1_000);
}
System.out.println("线程1:执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
/**
* 加上volatile后使box的修改可以得到通知
*
* @author Peter
*/
class Box_3{
private volatile List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}
4、优化wait和notify方法
解决方案:
- 线程1在nodify后必须要释放锁,那么就得wait了,然后线程2就可以获取到锁
- 然后线程2被唤醒后也必须再nodify通知线程2,虽然使用nodify唤醒了线程1,但线程2并没有释放锁,要5秒后线程2执行完释放了锁 线程1已才能获取到锁继续添加元素
package examination;
import java.util.ArrayList;
import java.util.List;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
*
* 这里使用wait()和notify()方法实现,
* 注意:wait()会释放锁,而nodify()不会释放锁。
*
* 该例中线程2必须比线程1先启动,才能保持监听状态
* 因为必须先wait,才有nodify。
*
* 但是本程序的输出还是没有达到预期效果:
* size=5的时候线程2没有继续往下执行,而是
* 等到线程1执行完毕后才往下执行的,因为
* nodify是不释放锁的,而wait被nodify后是需要
* 重新持有锁的,但是现在这把锁被线程1持有着
* 所以只能等到线程1离开同步代码块时,线程2
* 才能继续往下执行。
*
* 解决方案:
* 线程1在nodify后必须要释放锁,那么就得wait了,
* 然后线程2被唤醒后也必须再nodify通知线程2。
* @author xzq
*/
public class Examination04 {
public static void main(String[] args) {
final Box_4 box=new Box_4();
final Object obj=new Object();
new Thread("线程2"){
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("线程2:开始执行");
if(box.size()!=5){
obj.wait();
}
System.out.println("线程2:执行完毕!");
//通知线程2我已经被唤醒了,你也别等着了
obj.notify();
/*
上面notify只是唤醒了线程1,但我并没有释放锁
要5秒后我执行完释放了锁 线程1已才能获取到锁继续添加元素
*/
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread("线程1"){
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("线程1:开始执行");
for(int i=1;i<=10;i++){
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
if(box.size()==5){
obj.notify();
//释放锁,让线程2得以执行。
obj.wait();
}
Thread.sleep(1_000);
}
System.out.println("线程1:执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
/**
* 加上volatile后使box的修改可以得到通知
*
* @author xzq
*/
class Box_4{
private volatile List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}
5、使用CyclicBarrier方法解决
只有设置为2的时候才可以满足,设置3就不行
CyclicBarrier 是篱栅的意思,要等两个线程都执行完才能进行下一步
await()可以是线程进入barrier(障碍)状态,当两个线程都进入barrier状态时
两个线程继续执行下面的内容 不需再等待
final CyclicBarrier barrier=new CyclicBarrier(2);
barrier.await();
package examination;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
*
* 这里使用CountDownLatch/CyclicBarrier/Semaphore
* 都可以简便解决这个需求
* @author xzq
*/
public class Examination05 {
public static void main(String[] args) {
final Box_5 box=new Box_5();
/*
只有设置为2的时候才可以满足,设置3就不行
CyclicBarrier 是篱栅的意思,要等两个线程都执行完才能进行下一步
await()可以是线程进入barrier(障碍)状态,当两个线程都进入barrier状态时
两个线程继续执行下面的内容 不需再等待
*/
final CyclicBarrier barrier=new CyclicBarrier(2);
new Thread("线程2"){
@Override
public void run() {
try {
System.out.println("线程2:开始执行");
if(box.size()!=5){
barrier.await();
}
System.out.println("线程2:执行完毕!");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}.start();
new Thread("线程1"){
@Override
public void run() {
try {
System.out.println("线程1:开始执行");
for(int i=1;i<=10;i++){
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
if(box.size()==5){
barrier.await();
}
Thread.sleep(1_000);
}
System.out.println("线程1:执行完毕!");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}.start();
}
}
/**
* 加上volatile后使box的修改可以得到通知
*
* @author xzq
*/
class Box_5{
private volatile List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}
6、使用Semaphore 信号灯方法
package examination;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
/**
* taobao examination
* 实现一个容器,提供两个方法,add和size
* 写两个线程,线程1添加10个元素到容器中,
* 线程2实现监控元素的个数,当个数到5个时,
* 线程2给出提示并结束
*
* 这里使用CountDownLatch/CyclicBarrier/Semaphore
* 都可以简便解决这个需求
* @author xzq
*/
public class Examination06 {
public static void main(String[] args) {
final Box_6 box=new Box_6();
final Semaphore reachSema=new Semaphore(0);
// final Semaphore addSema=new Semaphore(10);
final Semaphore addSema=new Semaphore(1);
new Thread("线程2"){
@Override
public void run() {
try {
System.out.println("线程2:开始执行");
if(box.size()!=5){
reachSema.acquire();
}
System.out.println("线程2:执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("线程1"){
@Override
public void run() {
try {
System.out.println("线程1:开始执行");
for(int i=1;i<=10;i++){
addSema.acquire();
box.add(new Object());
System.out.println("线程1:add第"+i+"个元素");
if(box.size()==5){
reachSema.release();
}
addSema.release();
Thread.sleep(1_000);
}
// addSema.release();//这里是创建10个信号灯的实现
System.out.println("线程1:执行完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
/**
* 加上volatile后使box的修改可以得到通知
*
* @author xzq
*/
class Box_6{
private volatile List<Object> box=new ArrayList<>();
public void add(Object element){
box.add(element);
}
public int size(){
return box.size();
}
}