一、 概述
多个线程并发执行时,CPU是随机切换线程的,是无序的,当我们需要用多个线程来共同完成一个操作,就需要某种通信机制来协调线程,如果没有协调通信的机制,会造成多个线程对共享资源的争夺,会造成严重的数据污染问题。比如说现在共有5个苹果,A拿走5个同时B放进去3个,那么此时剩余的苹果可能是0、3、8,这就是线程间不通信造成的。
二、线程间的通信方式
1、synchronized对象锁
给某个对象加锁,锁住的是对象
synchronized关键字可以修饰在方法methodA()上,因为调用该方法是 Object.methodA(),所以修饰在方法上锁住的是对象object。
也可以锁住代码块,都是锁住某一个对象。

class Obj{
    public synchronized void methodA(){
    }
    public  void methodB(){
      synchronized(this){ 或这样 都是一样的 都是给Obj加锁
      //要执行这一块,必须要获取Obj的锁,如果被methodA先获取了,就要等methodA()执行结束之后,释放锁 才能执行
      }
    }
}

举例子:
模拟多线程对数据库中的一条数据同时修改,会引起数据覆盖的问题或者读脏数据的情况

public class Demo0907 {
    public static void main(String[] args) {
        User user = new User(10,"10","10");
        UserDao userDao1 = new UserDao(user);
        UserThread1 t1 = new UserThread1(userDao1);
        UserThread2 t2 = new UserThread2(userDao1);
        t1.start();
        t2.start(); 
    }
}
class UserThread1 extends Thread{
    private UserDao userDao;
    public UserThread1(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void run() {
        userDao.updateTwo("11",11,11);
    }
}
class UserThread2 extends Thread{
    private UserDao userDao;
    public UserThread2(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void run() {
        userDao.updateOne();
    }
}
class UserDao{
    private User user ; 
    public UserDao(User user) {
        this.user = user;
    }
    public synchronized void updateOne(){
        System.out.println(user.toString()+"---updateOne");
    }
    public synchronized void updateTwo(String name,int age,String sex){
        user.setName(name);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        user.setAge(age);
        user.setSex(sex);
        System.out.println(user.toString()+"---updateTwo");
    }
}
class User{
    private int age;
    private String sex;
    private String name ;
    //省略get set 构造器 toString 方法 
}

如果不加synchronized关键字 方法updateTwo只修改了一个参数就sleep了,此时updateOne读取的数据就会是(10,10,11),也就是数据不同步,正确的数据是(11,11,11)。
若多个线程拥有同一个MyObject类的对象,则这些方法只能以同步的方式执行。即,执行完一个synchronized修饰的方法后,才能执行另一个synchronized修饰的方法。
2、 wait()、notify/notifyAll()
wait() 与 notify/notifyAll() 是Object类的被final修饰的方法,不能被重写
wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。
执行过程:
当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
注意:notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程
例如:
生产者和消费者的问题。当库存为零时,生产者开始生产,当库存为5时,消费者开始消费。模拟生产20个 ,消费20个。

public class Demo0903 {
    public static void main(String[] args) {
        Store store = new Store();
        Productor p1 = new Productor(store);//构造多线程
        Consumer c1 = new Consumer(store);
        p1.setName("生产者-1");
        c1.setName("消费者-2");        
        p1.start();//开启多线程
        c1.start();
    }
}
class Productor extends Thread{
    private Store store;
    public Productor(Store store) {
         this.store=store;
    }
     @Override
    public void run() {
         store.pushApple();
    }
}
class Consumer extends Thread{
    private Store store;
     public Consumer(Store store) {
         this.store=store;
    }
     @Override
    public void run() {
         store.getApple();
    }
}
class Store{
    //共享资源库存
    private LinkedList<Apple>  store= new LinkedList<Apple>();
    public synchronized void pushApple(){//生产者放苹果
        synchronized(store){
            for(int i = 1;i<20;i++){
                Apple apple = new Apple(i);
                while (store.size()==5){//当库存中有5个苹果了,此时该线程进入等待状态,并释放store锁
                    try {
                        store.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }       
                store.addFirst(apple);
                System.out.println(Thread.currentThread().getName()+"存放:"+apple.toString());
                store.notifyAll();//此时唤醒消费者线程 唤醒之后也不会立即执行 因为获取不到锁 要等库存=5时 wait()释放锁之后  消费者线程才能执行
            }               
        }       
    }
    public  void getApple(){//消费者取苹果
        synchronized(store){
            for(int i = 1;i<20;i++){
                while (store.size()==0){
                    try {
                        store.wait(); //一开始,因为库存为0,所以该线程就进入了等待 等待被唤醒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Apple apple = store.removeFirst();
                System.out.println(Thread.currentThread().getName()+"吃掉:"+apple.toString());
                store.notifyAll();//唤醒生产者线程,直到消费者消费完了所有的苹果,进入wait()释放锁 ,唤醒的生产者线程才能执行
            }   
            }
    }
}
class Apple{
    private int id;
    public Apple(int id){
        this.id=id;
    }
    @Override
    public String toString() {
        return "苹果:"+id;
    }
}