您的位置:首页 > 大数据 > 人工智能

线程之间的同步和通信,synchronized,wait(),notify(),notifyAll()

2015-11-26 20:26 399 查看
最近因为项目的原因,涉及到了线程通信,下面是我学习的总结,不对的地方,欢迎大家指出来噢!

同步:synchronized

一提到线程,就会联想到同步,什么叫做同步呢?在什么样的情况下要用到同步呢?

在这之前,得先知道临界区;

多个线程竞争同一资源,我们把这资源叫做共享资源,当多个线程更新这共享资源时,就会引发竞态条件,我们把这共享资源的代码区,用synchronized建立,防止多个线程访问,这部分建立的代码就叫做临界区,在这种情况下,就需要同步了。

[code]    synchronized(object){
    //This code can be accessd
    //by only one task at a time
     }




[code]    public synchronized void f(){
    //This code can be accessd
    //by only one task at a time
     }


其实第二个指定的对象就是this;

同步:

多个线程对临界区进行访问,临界区的变量都从主存中读取,当线程退出临界区时,所有改变的变量都刷新回主存中;

[code]public class Synchronous {

    int number = 0;
    Object object = new Object();
    public void add(){
        ++number;
    }
    public class MyRunnable implements Runnable{

        @Override
        public void run() {
            while (number < 5) {
                synchronized (object) {
//                  if (number < 5) {
                        add();
                        System.out.println(Thread.currentThread().getName() + "  number=" + number);
//                  }
                }

            }
        }

    }
    public static void main(String[] args) {
        Synchronous synchronous = new Synchronous();
        MyRunnable myRunnable = synchronous.new MyRunnable();
        new Thread(myRunnable,"Thread 1").start();
        new Thread(myRunnable,"Thread 2").start();
        new Thread(myRunnable,"Thread 3").start();
    }

}


[code]Thread 1  number=1
Thread 1  number=2
Thread 1  number=3
Thread 1  number=4
Thread 1  number=5
Thread 3  number=6
Thread 2  number=7


每个线程更新的number都没有重复的值,这个就是同步的魅力!细心的人会发现,程序里面设置的不是number<5么,怎么输出number增加到7啦?当number=4时,三个线程都到了while语句里面,都在竞争object对象,Thread 1先竞争到了。当object对象释放了,立马有线程竞争,然后执行synchronized里面的代码;当Thread 1 退出同步块时,它检测到了number已经等于5啦,所以不会再执行了。

线程之间的通信

线程通信也就是线程之间传递信号;

线程间发送信号的一个简单方式可以在共享资源中的变量里设置信号值,比如两个线程A、B,A获取对象锁之后,给一个变量赋值,set(int number){},然后退出,当线程B获取到对象锁后,通过get()获取变量number的值;

在这里我们又发现了一个问题,当B先读取变量number的值,A后赋值时,那B不就没有获取到A赋的值了么?Java里面提供了wait(),notify(),notifyAll()就是解决这个问题的。

wait(),也就是等待,当A还没有赋值时,就让B一直等待,等A赋值完;

notify(),也就是唤醒,当A赋值完,A把正在等待的B唤醒;

生产者和消费者模式就是一个典型的例子。下面用的是Java编程思想里面的一个例子。

[code]class Meal {
  private final int orderNum;
  public Meal(int orderNum) { this.orderNum = orderNum; }
  public String toString() { return "Meal " + orderNum; }
}

class WaitPerson implements Runnable {
  private Restaurant restaurant;
  public WaitPerson(Restaurant r) { restaurant = r; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        synchronized(this) {
          while(restaurant.meal == null){
              System.out.println("waitperson");
            wait(); // ... for the chef to produce a meal

          }
        }
        print("Waitperson got " + restaurant.meal);
        synchronized(restaurant.chef) {
          restaurant.meal = null;
          restaurant.chef.notifyAll(); // Ready for another
        }
      }
    } catch(InterruptedException e) {
      print("WaitPerson interrupted");
    }
  }
}

class Chef implements Runnable {
  private Restaurant restaurant;
  private int count = 0;
  public Chef(Restaurant r) { restaurant = r; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        synchronized(this) {
          while(restaurant.meal != null)
            wait(); // ... for the meal to be taken
        }
        if(++count == 10) {
          print("Out of food, closing");
          restaurant.exec.shutdownNow();
        }
        print("Order up! ");
        synchronized(restaurant.waitPerson) {
          restaurant.meal = new Meal(count);
          restaurant.waitPerson.notifyAll();
        }
        TimeUnit.MILLISECONDS.sleep(100);
      }
    } catch(InterruptedException e) {
      print("Chef interrupted");
    }
  }
}

public class Restaurant {
  Meal meal ;
  ExecutorService exec = Executors.newCachedThreadPool();
  WaitPerson waitPerson = new WaitPerson(this);
  Chef chef = new Chef(this);
  public Restaurant() {
    exec.execute(chef);
    exec.execute(waitPerson);
  }
  public static void main(String[] args) {
    new Restaurant();
  }
}


细心的你,会发现为什么中的等待要用while循环呢,用if就可以啦!这样做是为了防止莫名其妙被唤醒,但是条件还是while里面的条件,这时被唤醒之后,需要再次检查下是不是不是等待的条件了,如果是等待的条件,那再次变成等待状态!这个地方需要好好琢磨下!

而notifyAll(),当有多个线程等待同一个锁时,而不能Java中不能指定唤醒哪个线程,这时我们用notifyAll(),把等待这个锁的线程都唤醒!

这篇就写到这儿啦,我表达得不好,如果有不清楚的地方或者错误的地方,欢迎留言!相互学习,嘻嘻
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: