有关线程互斥与同步面试题

来源:互联网 发布:定做软件要多少钱 编辑:程序博客网 时间:2024/05/04 06:12

一.一道经典的面试题

  • 关于线程的同步通信和互斥,我们可以用一道面试题来说明和检测,如下

子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次

  • 我们先实现子线程和主线程循环的代码
               new Thread(new Runnable() {                        @Override                        public void run() {                            for(int i=1;i<=50;i++) {                                for(int j=1;j<=10;j++) {                                    System.out.println("sub thread sequece of "+j+",loop of "+i);                                }                            }                        }                    }).start();                    for(int i=1;i<=50;i++) {                        for(int j=1;j<=100;j++) {                            System.out.println("main thread sequece of "+j+",loop of "+i);                        }                    }
  • 显然此时连互斥还没实现

这里写图片描述

  • 于是我们加上线程互斥
new Thread(new Runnable() {                        @Override                        public void run() {                            for(int i=1;i<=50;i++) {                                synchronized (TraditionalThreadCommunicationTest.class) {                                for(int j=1;j<=10;j++) {                                    System.out.println("sub thread sequece of "+j+",loop of "+i);                                }                                }                            }                        }                    }).start();                    for(int i=1;i<=50;i++) {                        synchronized (TraditionalThreadCommunicationTest.class) {                        for(int j=1;j<=100;j++) {                            System.out.println("main thread sequece of "+j+",loop of "+i);                        }                    }                    }

这里写图片描述

  • 虽然此时互斥不会有问题,但是并没有实现子线程一下,主线程一下的效果(即相互通信)

  • 而且还有一个重要的问题:上面这个代码真的好嘛???真的准确嘛???

  • 即使对于已经实现的功能来说,上面的代码也还有很多的问题

a.因为字节码文件只有一份,所以我们经常把它当成锁对象,但是这样真的好嘛?

  • 如果你项目很大,很复杂,要你分为A,B两组,A要同步,B也要同步,如果此时动用字节码文件来当锁对象,其中的同步块就变成一组了,所以此时是不能用其当锁对象,把字节码文件当锁对象,一定要慎之又慎!!!

b.我们要学会面向对象的思维的设计,应该要把相类似的方法放到一个类去,这样更容易维护一些,所以…

public class TraditionalThreadCommunicationTest {                public static void main(String[] args) {                    Business business = new Business();                    new Thread(new Runnable() {                        @Override                        public void run() {                            for(int i=1;i<=50;i++) {                                  business.sub(i);                            }                        }                    }).start();                    for(int i=1;i<=50;i++) {                           business.main(i);                    }                }}    class Business{           public synchronized void sub(int i) {               for(int j=1;j<=10;j++) {                    System.out.println("sub thread sequece of "+j+",loop of "+i);                }           }           public synchronized void main(int i) {               for(int j=1;j<=100;j++) {                    System.out.println("main thread sequece of "+j+",loop of "+i);                }           }    }
  • 当然这个和前一个代码功能一样的,也是互斥做到了,但是相互通信没有做到

  • 我们要用一个boolean值来进行下面的相互通信操作

public class TraditionalThreadCommunicationTest {                public static void main(String[] args) {                    Business business = new Business();                    new Thread(new Runnable() {                        @Override                        public void run() {                            for(int i=1;i<=50;i++) {                                  business.sub(i);                            }                        }                    }).start();                    for(int i=1;i<=50;i++) {                           business.main(i);                    }                }}    class Business{           private boolean flag=true;           public synchronized void sub(int i) {               if(!flag) {//如果为false,此时线程等待                   try {                    this.wait();//线程等待                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }               }               for(int j=1;j<=10;j++) {                    System.out.println("sub thread sequece of "+j+",loop of "+i);                }                flag=false;                this.notify();//唤醒等待的线程           }           public synchronized void main(int i) {               if(flag) {//为true,此时线程等待                   try {                    this.wait();//线程等待                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }               }               for(int j=1;j<=100;j++) {                    System.out.println("main thread sequece of "+j+",loop of "+i);                }               flag=true;               this.notify();//唤醒等待的线程           }    }
  • 这样就实现了上面的要求,基本没有问题了

  • 但是上面的代码还是有一些小小的问题

    • 有时候,线程并没有收到通知就被唤醒,所以此时用if并不好,可以把if改为while
    • 效果和if一样,但是比if更牢靠,更健壮
public class TraditionalThreadCommunicationTest {                public static void main(String[] args) {                    Business business = new Business();                    new Thread(new Runnable() {                        @Override                        public void run() {                            for(int i=1;i<=50;i++) {                                  business.sub(i);                            }                        }                    }).start();                    for(int i=1;i<=50;i++) {                           business.main(i);                    }                }}    class Business{           private boolean flag=true;           public synchronized void sub(int i) {               while(!flag) {                   try {                    this.wait();//线程等待                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }               }               for(int j=1;j<=10;j++) {                    System.out.println("sub thread sequece of "+j+",loop of "+i);                }                flag=false;                this.notify();//唤醒等待的线程           }           public synchronized void main(int i) {               while(flag) {                   try {                    this.wait();//线程等待                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }               }               for(int j=1;j<=100;j++) {                    System.out.println("main thread sequece of "+j+",loop of "+i);                }               flag=true;               this.notify();//唤醒等待的线程           }    }