线程间通信

wait / notify

package SynchronizedCass;
/*
 * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步
 */
public class t6 {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
            
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class MyThread1 extends Thread    {
    private Object lock;
    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized(lock) {
                System.out.println("开始     wait time = " + System.currentTimeMillis());
                lock.wait();        
                System.out.println("结束     wait time = " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }    
    }
}

class MyThread2 extends Thread    {
    private Object lock;
    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            System.out.println("开始     notify time = " + System.currentTimeMillis());
            lock.notify();    
            System.out.println("结束     notify time = " + System.currentTimeMillis());
        }
    }
}
开始     wait time = 1603525965724
开始     notify time = 1603525968732
结束     notify time = 1603525968732
结束     wait time = 1603525968732

wait使线程停止运行,而notify使停止的线程继续运行。

wait()方法的作用是使当前执行代码的线程进行等待,将其置入“预执行队列”中。wait所在代码行处停止执行,直到接到通知或被中断为止,线程与其他线程竞争锁。

注意:在调用wait之前,线程必须获得该对象的对象级别锁(也就是说,只能在同步方法或同步快中调用wait()方法);如果调用wait()时没有持有适当的锁,则会抛出异常。

notify()方法也要在同步方法或者同步块中调用,即在调用前,线程必须获得该对象的对象级别锁。如果有多个线程等待,则由线程规划器随机挑选出其中的一个呈wait状态的线程,对其发出notify,并使它等待获取该对象的对象锁。特别说明:执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify方法的线程将程序执行完,也就是退出synchronized代码块之后,当前线程才会释放锁。

分析上述例子可以发现:第二个线程将第一个线程唤醒,不过要等到第二个线程中的synchronized同步代码块中的代码执行完,才能返回到第一个线程的wait()处。

 

package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

/*
 * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步
 */
public class t6 {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
            
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class Mylist {
    private static List<String> list = new ArrayList<>();
    
    public static void add() {
        list.add("anyString");
    }
    
    public static int size() {
        return list.size();
    }
}



class MyThread1 extends Thread    {
    private Object lock;
    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized(lock) {
                if(Mylist.size() != 5) {
                    System.out.println("开始     wait time = " + System.currentTimeMillis());
                    lock.wait();        
                    System.out.println("结束     wait time = " + System.currentTimeMillis());                    
                }
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }    
    }
}



class MyThread2 extends Thread    {
    private Object lock;
    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            for(int i = 0; i < 10; i++) {
                Mylist.add();
                if(Mylist.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出通知!");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }
}









package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

/*
 * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步
 */
public class t6 {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
            
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class Mylist {
    private static List<String> list = new ArrayList<>();
    
    public static void add() {
        list.add("anyString");
    }
    
    public static int size() {
        return list.size();
    }
}



class MyThread1 extends Thread    {
    private Object lock;
    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized(lock) {
                if(Mylist.size() != 5) {
                    System.out.println("开始     wait time = " + System.currentTimeMillis());
                    lock.wait();        
                    System.out.println("结束     wait time = " + System.currentTimeMillis());                    
                }
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }    
    }
}



class MyThread2 extends Thread    {
    private Object lock;
    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            for(int i = 0; i < 10; i++) {
                Mylist.add();
                if(Mylist.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出通知!");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }
}
package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

/*
 * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步
 */
public class t6 {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
            
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class Mylist {
    private static List<String> list = new ArrayList<>();
    
    public static void add() {
        list.add("anyString");
    }
    
    public static int size() {
        return list.size();
    }
}



class MyThread1 extends Thread    {
    private Object lock;
    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized(lock) {
                if(Mylist.size() != 5) {
                    System.out.println("开始     wait time = " + System.currentTimeMillis());
                    lock.wait();        
                    System.out.println("结束     wait time = " + System.currentTimeMillis());                    
                }
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }    
    }
}



class MyThread2 extends Thread    {
    private Object lock;
    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            for(int i = 0; i < 10; i++) {
                Mylist.add();
                if(Mylist.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出通知!");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }
}









package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

/*
 * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步
 */
public class t6 {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
            
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class Mylist {
    private static List<String> list = new ArrayList<>();
    
    public static void add() {
        list.add("anyString");
    }
    
    public static int size() {
        return list.size();
    }
}



class MyThread1 extends Thread    {
    private Object lock;
    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized(lock) {
                if(Mylist.size() != 5) {
                    System.out.println("开始     wait time = " + System.currentTimeMillis());
                    lock.wait();        
                    System.out.println("结束     wait time = " + System.currentTimeMillis());                    
                }
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }    
    }
}



class MyThread2 extends Thread    {
    private Object lock;
    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock) {
            for(int i = 0; i < 10; i++) {
                Mylist.add();
                if(Mylist.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出通知!");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }
}
开始     wait time = 1603528243105
添加了1个元素!
添加了2个元素!
添加了3个元素!
添加了4个元素!
已经发出通知!
添加了5个元素!
添加了6个元素!
添加了7个元素!
添加了8个元素!
添加了9个元素!
添加了10个元素!
结束     wait time = 1603528256153

wait end 在最后输出,这也说明notify()方法执行后并不立即释放锁。

如果发出notify操作时没有处于阻塞状态的线程,那么该命令会被忽略。

关键字synchronized可以将任何一个Object对象作同步对象看待,而java为每个Object都实现了wait和notify,他们必须用在被synchronized同步的Object的临界区内。

线程状态切换示意图

这张图常常考,背吧

 

方法wait()锁自动释放与方法notify()锁不释放

package SynchronizedCass;

/*
 * wait方法自动释放,notify不自动释放锁
 */
public class t4 {
    public static void main(String[] args) {
        Object lock = new Object();
        ThreadC a = new ThreadC(lock);
        a.setName("进程1");
        a.start();
        
        ThreadD b = new ThreadD(lock);
        b.setName("进程2");
        b.start();
        
        synNotifyMethodThread c = new synNotifyMethodThread(lock);
        c.setName("进程3");
        c.start();
    }
}

class service1 {
    public void testMethod(Object lock) {
        try {
            synchronized(lock) {
                System.out.println("begin wait() ThreadName = " + Thread.currentThread().getName());
                lock.wait();
                System.out.println("  end wait() ThreadName = " + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
    
    public void synNotifyMethod(Object lock) {
        try {
            synchronized(lock) {
                System.out.println("begin notify() ThreadName = " + Thread.currentThread().getName() +
                        " time = " + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName = " + Thread.currentThread().getName() + 
                        " time = " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

class ThreadC extends Thread {
    private Object lock;
    public ThreadC(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.testMethod(lock);
    }
}

class ThreadD extends Thread {
    private Object lock;
    public ThreadD(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.synNotifyMethod(lock);
    }
}

class synNotifyMethodThread extends Thread {
    private Object lock;
    public synNotifyMethodThread(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.synNotifyMethod(lock);
    }
}
begin wait() ThreadName = 进程1
begin notify() ThreadName = 进程2 time = 1603532556469
  end notify() ThreadName = 进程2 time = 1603532561472
  end wait() ThreadName = 进程1
begin notify() ThreadName = 进程3 time = 1603532561472
  end notify() ThreadName = 进程3 time = 1603532566473

这个实验说明,必须执行完notify()方法所在的同步synchronized代码块后才释放锁

还有在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。

package SynchronizedCass;
/*
 * 调用方法notify一次只随机通知一个线程进行唤醒
 */
public class t4 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        ThreadC a = new ThreadC(lock);
        a.setName("进程1");
        a.start();
        
        ThreadD b = new ThreadD(lock);
        b.setName("进程2");
        b.start();
        
        ThreadE c = new ThreadE(lock);
        c.setName("进程3");
        c.start();
        
        NotifyThread notifyThread = new NotifyThread(lock);
        notifyThread.setName("进程4");
        notifyThread.start();
    }
}

class service1 {
    public void testMethod(Object lock) {
        try {
            synchronized(lock) {
                System.out.println("begin wait() ThreadName = " + Thread.currentThread().getName());
                lock.wait();
                System.out.println("  end wait() ThreadName = " + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
    
//    public void synNotifyMethod(Object lock) {
//        try {
//            synchronized(lock) {
//                System.out.println("begin notify() ThreadName = " + Thread.currentThread().getName() +
//                        " time = " + System.currentTimeMillis());
//                lock.notify();
//                Thread.sleep(5000);
//                System.out.println("  end notify() ThreadName = " + Thread.currentThread().getName() + 
//                        " time = " + System.currentTimeMillis());
//            }
//        } catch (InterruptedException e) {
//            // TODO 自动生成的 catch 块
//            e.printStackTrace();
//        }
//    }
}

class ThreadC extends Thread {
    private Object lock;
    public ThreadC(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.testMethod(lock);
    }
}

class ThreadD extends Thread {
    private Object lock;
    public ThreadD(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.testMethod(lock);
    }
}

class ThreadE extends Thread {
    private Object lock;
    public ThreadE(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        super.run();
        service1 ser = new service1();
        ser.testMethod(lock);
    }
}

class NotifyThread extends Thread {
    private Object lock;
    public NotifyThread(Object lock) {
        super();
        this.lock = lock;
    }
    
    @Override
    public void run() {
        synchronized (lock) {
            lock.notify();
            lock.notify();
            lock.notify();
        }
    }
}
begin wait() ThreadName = 进程1
begin wait() ThreadName = 进程2
begin wait() ThreadName = 进程3
  end wait() ThreadName = 进程1
  end wait() ThreadName = 进程3
  end wait() ThreadName = 进程2

 调用方法notify()一次只能随机通知一个线程唤醒。当多次调用nitify方法时,会随机将等待wait状态的线程进行唤醒。为了唤醒全部线程,可以使用notifyAll()方法。

 

方法wait(long)的使用

带一个参数的wait(long)方法的功能是:等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。注意一个问题,如果notify通知过早,则会打乱程序正常的运行逻辑。

 

下面讲解生产者-消费者问题

package SynchronizedCass;


public class P {
    public static void main(String[] args) {
        String lock = new String("");
        
        Product p = new Product(lock);
        Consume c = new Consume(lock);
        
        ThreadP threadp = new ThreadP(p);
        ThreadCo threadc = new ThreadCo(c);
        
        threadp.start();
        threadc.start();
    }
}    

//生产者
class Product {
    private String lock;
    public Product(String lock) {
        super();
        this.lock = lock;
    }
    
    public void setValue() {
        try {
            synchronized (lock) {
                if(!ValueObject.value.equals("")) {
                    lock.wait();    //如果ValueObject非空,说明有对象,就暂停生产
                }
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("set的值是" + value);
                ValueObject.value = value;
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//消费者
class Consume {
    private String lock;
    public Consume(String lock) {
        super();
        this.lock = lock;
    }
    
    public void getValue() {
        try {
            synchronized(lock) {
                if(ValueObject.value.equals("")) {
                    lock.wait();
                }
                System.out.println("get 的值是 " + ValueObject.value);
                ValueObject.value = "";
                lock.notify();    //消费者取走后,要唤醒生产者继续生产
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    
}

//创建存储值的对象ValueObejct.java
class ValueObject {
    public static String value = " ";
    
}

//生产者进程
class ThreadP extends Thread {
    private Product p;
    
    public ThreadP(Product p) {
        // TODO 自动生成的构造函数存根
        super();
        this.p = p;
    }
    
    @Override
    public void run() {
        while(true) {
            p.setValue();
        }
    }
}

//消费者进程
class ThreadCo extends Thread {
    private Consume c;
    
    public ThreadCo(Consume c) {
        super();
        this.c = c;
    }
    
    @Override
    public void run() {
        while(true) {
            c.getValue();
        }
    }
}
set的值是1603617803974_718699827459900
get 的值是 1603617803974_718699827459900
set的值是1603617803974_718699827472100
get 的值是 1603617803974_718699827472100
set的值是1603617803974_718699827484400
get 的值是 1603617803974_718699827484400
set的值是1603617803974_718699827496400
get 的值是 1603617803974_718699827496400
set的值是1603617803974_718699827509400
...

 

从结果可以看出,本例子是1个生产者和1个消费者进行数据交互,控制台打印的日志get和set是小题运行的。

 

多生产与多消费:操作值-假死

”假死“的现象其实就是线程进入WAITING等待状态。如果全部线程都进入WAITING状态,则程序就不再执行任何业务功能了,整个项目呈停止状态。

简单分析一下:在代码中已经使用了wait/notify,但不保证notify唤醒的是异类,也许是同类,比如“生产者”唤醒“生产者”,或“消费者”唤醒“消费者这样的情况”。这样积少成多,就会导致所有的线程都不能执行下去,大家都处于等待的状态。解决的办法是将notify()改成notifyAll方法即可,他就不光通知同类线程,也包括异类。

 

一生产与一消费——栈操作

package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

public class P {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        
        Product p = new Product(myStack);
        Consume c = new Consume(myStack);
        
        ThreadP threadp = new ThreadP(p);
        ThreadCo threadc = new ThreadCo(c);
        
        threadp.start();
        threadc.start();
    }
}    

/*list的最大容量是1*/
class MyStack {
    private List<String> list = new ArrayList<>();
    
    synchronized public void push() {
        try {
            if(list.size() == 1) {
                this.wait();
            }
            list.add("anyString = " + Math.random());
            this.notify();
            System.out.println("push = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    synchronized public String pop() {
        String returnValue = "";
        try {
            if(list.size() == 0) {
                System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态");
                this.wait();
            }
            
            returnValue = "" + list.get(0);
            list.remove(0);
            this.notify();
            System.out.println("pop = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}

//生产者
class Product {
    private MyStack myStack;
    public Product(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void pushService() {
        myStack.push();
    }
}

//消费者
class Consume {
    private MyStack myStack;
    
    public Consume(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void popService() {
        System.out.println("pop = " + myStack.pop());
    }
        
}


//生产者进程
class ThreadP extends Thread {
    private Product p;
    
    public ThreadP(Product p) {
        // TODO 自动生成的构造函数存根
        super();
        this.p = p;
    }
    
    @Override
    public void run() {
        while(true) {
            p.pushService();
        }
    }
}

//消费者进程
class ThreadCo extends Thread {
    private Consume c;
    
    public ThreadCo(Consume c) {
        super();
        this.c = c;
    }
    
    @Override
    public void run() {
        while(true) {
            c.popService();
        }
    }
}
pop = anyString = 0.08250087230110892
push = 1
pop = 0
pop = anyString = 0.5222318002615051
push = 1
pop = 0
pop = anyString = 0.646779057439545
push = 1
pop = 0
pop = anyString = 0.6995607244467061
push = 1
pop = 0

通过使用生产者/消费者模式,本例的结果,值在0和1之间进行交替,也就是生产和消费两个过程在交替执行。

 

package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

public class P {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        
        Product p = new Product(myStack);
    
        Consume c = new Consume(myStack);
        Consume c1 = new Consume(myStack);
        Consume c2 = new Consume(myStack);
        Consume c3 = new Consume(myStack);
        
        ThreadP threadp = new ThreadP(p);

        ThreadCo threadc = new ThreadCo(c);
        ThreadCo threadc1 = new ThreadCo(c1);
        ThreadCo threadc2 = new ThreadCo(c2);
        ThreadCo threadc3 = new ThreadCo(c3);

        
        threadp.start();
        
        threadc.start();
        threadc1.start();
        threadc2.start();
        threadc3.start();
    }
}    

/*list的最大容量是1*/
class MyStack {
    private List<String> list = new ArrayList<>();
    
    synchronized public void push() {
        try {
            while(list.size() == 1) {
                this.wait();
            }
            list.add("anyString = " + Math.random());
            this.notifyAll();
            System.out.println("push = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    synchronized public String pop() {
        String returnValue = "";
        try {
            while(list.size() == 0) {
                System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态");
                this.wait();
            }
            
            returnValue = "" + list.get(0);
            list.remove(0);
            this.notifyAll();
            System.out.println("pop = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}

//生产者
class Product {
    private MyStack myStack;
    public Product(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void pushService() {
        myStack.push();
    }
}

//消费者
class Consume {
    private MyStack myStack;
    
    public Consume(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void popService() {
        System.out.println("pop = " + myStack.pop());
    }
        
}


//生产者进程
class ThreadP extends Thread {
    private Product p;
    
    public ThreadP(Product p) {
        // TODO 自动生成的构造函数存根
        super();
        this.p = p;
    }
    
    @Override
    public void run() {
        while(true) {
            p.pushService();
        }
    }
}

//消费者进程
class ThreadCo extends Thread {
    private Consume c;
    
    public ThreadCo(Consume c) {
        super();
        this.c = c;
    }
    
    @Override
    public void run() {
        while(true) {
            c.popService();
        }
    }
}
pop操作中的:Thread-1线程呈wait状态
push = 1
pop = 0
pop = anyString = 0.9837796821362911
pop操作中的:Thread-2线程呈wait状态
pop操作中的:Thread-3线程呈wait状态
pop操作中的:Thread-4线程呈wait状态
pop操作中的:Thread-1线程呈wait状态
push = 1
pop = 0
pop = anyString = 0.2304706772528935
pop操作中的:Thread-4线程呈wait状态
pop操作中的:Thread-3线程呈wait状态
pop操作中的:Thread-2线程呈wait状态
pop操作中的:Thread-1线程呈wait状态
push = 1
pop = 0
pop = anyString = 0.10921236392571365
。。。

注意:

①、if(list.size() == 0)这个wait()条件,可能会导致程序发生异常。因为条件发生改变时并没有得到及时的响应,所以多个呈现wait状态的线程先后被唤醒,继而执行list.remove(0)代码而出现异常。举一个例子:现在c,c1,c2,c3四个消费这进程处于wait状态,生产者进程p给list加了一个元素后,list中有且只有一个元素。而p唤醒了c,c进程remove了一个元素,然后唤醒了c1,c1退出条件,也remove一个元素。但是list中的元素不超过1,所以会出现索引异常。正确的做法是把if(list.size() == 0)==========>换成while(list.size() == 0) ,退出wait还要再判断能否取list中的元素。

还需要注意的可能会发生“假死”的现象,导致所有进程都处于WAITING,所以最好将notify改为notifyAll。

 

多生产与多消费:栈操作

多生产多消费的核心代码同一生产多消费,只需要在添加几个生产者进程即可。

package SynchronizedCass;

import java.util.ArrayList;
import java.util.List;

public class P {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        
        Product p = new Product(myStack);
        Product p1 = new Product(myStack);
        Product p2 = new Product(myStack);
        Product p3 = new Product(myStack);
    
        Consume c = new Consume(myStack);
        Consume c1 = new Consume(myStack);
        Consume c2 = new Consume(myStack);
        Consume c3 = new Consume(myStack);
        
        ThreadP threadp = new ThreadP(p);
        ThreadP threadp1 = new ThreadP(p1);
        ThreadP threadp2 = new ThreadP(p2);
        ThreadP threadp3 = new ThreadP(p3);
        
        ThreadCo threadc = new ThreadCo(c);
        ThreadCo threadc1 = new ThreadCo(c1);
        ThreadCo threadc2 = new ThreadCo(c2);
        ThreadCo threadc3 = new ThreadCo(c3);

        
        threadp.start();
        threadp1.start();
        threadp2.start();
        threadp3.start();
        
        threadc.start();
        threadc1.start();
        threadc2.start();
        threadc3.start();
    }
}    

/*list的最大容量是1*/
class MyStack {
    private List<String> list = new ArrayList<>();
    
    synchronized public void push() {
        try {
            while(list.size() == 1) {
                this.wait();
            }
            list.add("anyString = " + Math.random());
            this.notifyAll();
            System.out.println("push = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    synchronized public String pop() {
        String returnValue = "";
        try {
            while(list.size() == 0) {
                System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态");
                this.wait();
            }
            
            returnValue = "" + list.get(0);
            list.remove(0);
            this.notifyAll();
            System.out.println("pop = " + list.size());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}

//生产者
class Product {
    private MyStack myStack;
    public Product(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void pushService() {
        myStack.push();
    }
}

//消费者
class Consume {
    private MyStack myStack;
    
    public Consume(MyStack myStack) {
        super();
        this.myStack = myStack;
    }
    
    public void popService() {
        System.out.println("pop = " + myStack.pop());
    }
        
}


//生产者进程
class ThreadP extends Thread {
    private Product p;
    
    public ThreadP(Product p) {
        // TODO 自动生成的构造函数存根
        super();
        this.p = p;
    }
    
    @Override
    public void run() {
        while(true) {
            p.pushService();
        }
    }
}

//消费者进程
class ThreadCo extends Thread {
    private Consume c;
    
    public ThreadCo(Consume c) {
        super();
        this.c = c;
    }
    
    @Override
    public void run() {
        while(true) {
            c.popService();
        }
    }
}
pop操作中的:Thread-4线程呈wait状态
push = 1
pop = 0
pop = anyString = 0.9242501412687139
pop操作中的:Thread-6线程呈wait状态
pop操作中的:Thread-5线程呈wait状态
pop操作中的:Thread-7线程呈wait状态
pop操作中的:Thread-4线程呈wait状态
push = 1
pop = 0
pop = anyString = 0.0698453157466532
pop操作中的:Thread-7线程呈wait状态
pop操作中的:Thread-5线程呈wait状态
pop操作中的:Thread-6线程呈wait状态
pop操作中的:Thread-4线程呈wait状态
push = 1
pop = 0

 

实战:等待/通知

创建20个线程,其中10个线程是将数据备份到A数据库,另外10个线程是将数据备份到B数据库,然后备份A数据库和备份B数据库交叉进行。

 

 

package SynchronizedCass;

public class wait_notify_insert_test {
    public static void main(String[] args) {
        DBTools dbtools = new DBTools();

        BackupA[] threadA = new BackupA[10];
        BackupB[] threadB = new BackupB[10];
        
        
        for(int i = 0; i < 10; i++) {
            threadA[i] = new BackupA(dbtools);
            threadA[i].start();
            
            threadB[i] = new BackupB(dbtools);
            threadB[i].start();
        }
    }
}


class DBTools {
    //prevIsA = true 表示前一个星串是数据库A首先执行的,那么下次就换成数据库B执行
    volatile private boolean prevIsA = false;
    
    synchronized public void backupA() {
        try {
            while(prevIsA == true) {
                wait();
            }
            for(int i = 0; i < 5; i++) {
                System.out.println("★★★★★★★★★★");
            }
            
            prevIsA = true;
            notifyAll();
            
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    synchronized public void backupB() {
        try {
            while(prevIsA == false) {
                wait();
            }
            for(int i = 0; i < 5; i++) {
                System.out.println("☆☆☆☆☆☆☆☆☆☆");
            }
            
            prevIsA = false;
            notifyAll();
            
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class BackupA extends Thread {
    private DBTools dbtools;
    
    public BackupA(DBTools dbtools) {
        this.dbtools = dbtools;
    }
    
    @Override
    public void run() {
        super.run();
        dbtools.backupA();
    }
}

class BackupB extends Thread {
    private DBTools dbtools;
    
    public BackupB(DBTools dbtools) {
        this.dbtools = dbtools;
    }
    
    @Override
    public void run() {
        super.run();
        dbtools.backupB();
    }
}
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
☆☆☆☆☆☆☆☆☆☆
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★
★★★★★★★★★★

打印输出是交替的,这里使用了valatile关键字使得prevIsA对20个线程之间是可见的。

 

join方法的使用

使用背景:在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将遭遇子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束(比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join方法)。

下面看一个例子:

package SynchronizedCass;

public class t7 {
    public static void main(String[] args) {
        try {
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join();
            System.out.println("我想当ThreadTest对象执行完毕后,我再执行,我做到了!");
            
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        try {
            int secondValue = (int) (Math.random() * 10000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
            System.out.println("这次我ThreadTest对象时真的执行完了!");
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
ThreadTest对象要等待5858毫秒。
这次我ThreadTest对象时真的执行完了!
我想当ThreadTest对象执行完毕后,我再执行,我做到了!

从实验结果上来看,threadTest.join()的确使得主线程在子线程执行完毕以后在执行后面的代码。我们总结一下join方法:

方法join方法使 所属的线程x正常执行run()方法中的任务,而使当前线程y无限期的阻塞,等待线程x销毁以后再继续执行线程y后面的代码。

可以看出方法join()具有使线程排队运行的作用,有点类似于同步运行。但是与synchronized还有些区别:

  1. join在内部使用了wait()方法进行等待,
  2. synchronized关键字使用的是“对象监视器”原理,来同步的。

3.2.5  方法join(long)与sleep(long)的区别

先看两个例子:

package SynchronizedCass;

public class t9 {
    public static void main(String[] args) {
        try {
            ThreadBB b = new ThreadBB();
            ThreadAA a = new ThreadAA(b);
            
            a.start();
            Thread.sleep(1000);
            
            ThreadCC c = new ThreadCC(b);
            c.start();
            
        }catch (Exception e) {
            // TODO: handle exception
        }
    }
}

class ThreadAA extends Thread {
    private ThreadBB b;
    
    public ThreadAA(ThreadBB b) {
        this.b = b;
    }
    
    @Override
    public void run() {
        try {
            synchronized (b) {
                b.start();
                Thread.sleep(6000);//不释放锁
                //b.join(6000);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadCC extends Thread {
    private ThreadBB threadB;
    
    public ThreadCC(ThreadBB threadb) {
        this.threadB = threadb;
    }
    
    @Override
    public void run() {
        threadB.bService();
    }
}

class ThreadBB extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("  b run begin timer = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("  b run end timer = " + System.currentTimeMillis());
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();    
        }
    }
    
    synchronized public void bService() {
        System.out.println("打印了 BService timer = " + System.currentTimeMillis());
    }
}
  b run begin timer = 1603877992713
  b run end timer = 1603877997713
打印了 BService timer = 1603877998723
package SynchronizedCass;

public class t9 {
    public static void main(String[] args) {
        try {
            ThreadBB b = new ThreadBB();
            ThreadAA a = new ThreadAA(b);
            
            a.start();
            Thread.sleep(1000);
            
            ThreadCC c = new ThreadCC(b);
            c.start();
            
        }catch (Exception e) {
            // TODO: handle exception
        }
    }
}

class ThreadAA extends Thread {
    private ThreadBB b;
    
    public ThreadAA(ThreadBB b) {
        this.b = b;
    }
    
    @Override
    public void run() {
        try {
            synchronized (b) {
                b.start();
                //Thread.sleep(6000);//不释放锁
                b.join(6000);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadCC extends Thread {
    private ThreadBB threadB;
    
    public ThreadCC(ThreadBB threadb) {
        this.threadB = threadb;
    }
    
    @Override
    public void run() {
        threadB.bService();
    }
}

class ThreadBB extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("  b run begin timer = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("  b run end timer = " + System.currentTimeMillis());
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();    
        }
    }
    
    synchronized public void bService() {
        System.out.println("打印了 BService timer = " + System.currentTimeMillis());
    }
}
  b run begin timer = 1603878064069
打印了 BService timer = 1603878065081
  b run end timer = 1603878069077

 对比两个结果可以发现:线程a使用了join(long)时,他会释放b的锁,所以线程c可以调用b中的同步方法synchronized public void bService()。

故当执行join(long),会在内部使用wait(long)方法来实现,所以join(long)会有释放锁的特点,那么其他线程就可以调用此线程中的同步方法了。但是Thread.sleep(long)方法却不会释放锁。

 

类ThreadLocal的使用

类ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal类中进行保存的。线程局部变量(ThreadLocal)为每一个使用该变量的线程都提供一个变量值的副本。

package SynchronizedCass;

import java.util.Date;

public class t9 {
    public static void main(String[] args) {
        try {
            ThreadAA a = new ThreadAA();
            a.setName("a");
            a.start();    
            Thread.sleep(1000);
            ThreadBB b = new ThreadBB();
            b.setName("b");
            b.start();
            Thread.sleep(1000);
        }catch (InterruptedException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

class ThreadAA extends Thread {
    @Override
    public void run() {
        try {
            for(int i = 0; i < 10; i++) {
                if(Tools.tl.get() == null) {
                    Tools.tl.set(new Date());
                }
                System.out.println(Thread.currentThread().getName() + " " + Tools.tl.get().getTime());
                Thread.sleep(100);
            }
        }catch (InterruptedException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}


class ThreadBB extends Thread {
    @Override
    public void run() {
        try {
            for(int i = 0; i < 10; i++) {
                if(Tools.tl.get() == null) {
                    Tools.tl.set(new Date());
                }
                System.out.println(Thread.currentThread().getName() + " " +Tools.tl.get().getTime());
                Thread.sleep(100);
            }
        }catch (InterruptedException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

class Tools    {
    public static ThreadLocal<Date> tl = new ThreadLocal<>();
}
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
a 1603889652770
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776
b 1603889653776

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-10-25 17:35  Peterxiazhen  阅读(93)  评论(0编辑  收藏  举报