本文最后更新于752 天前,其中的信息可能已经过时,如有错误请发送邮件到434658198@qq.com
1、下面程序会有什么问题
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不会得到通知,不会结束
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方法
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:开始执行");
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方法
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();
} 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方法解决
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();
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();
}
}