线程间的通信

线程间通信——等待唤醒机制

wait(), notify(), notifyAll(), 都使用在同步中,因为要对持有监视器(锁)的线程操作。

思考:为什么操作线程的方法wait notify notifyAll定义在了Object类中? 

因为这些方法是监视器的方法。监视器其实就是锁。锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。


1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。

2,notify():唤醒线程池中一个线程(任意).

3,notifyAll():唤醒线程池中的所有线程。


wait() sleep() 有什么区别?

wait() :释放资源,释放锁 。

sleep() :释放资源,不释放锁。

/*

线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同(一个是wait 一个是notify)。*/

/*

定义了一个资源类。

里面有俩个个同步函数input(String name,String user) output() 使用的锁是this.

*/

class Res{

    private String name;//定义了一个名字,用于把传递进行的名字赋值给name变量

    private String user;//定义了一个信息,用于把传递进来的内容赋值给user变量

    private boolean flag;//为了线程的通讯,定义了一个标记,默认值是false

    private int count  =  100;//定义了一个共享数据,由于资源是唯一的,所以不用被static修饰

    /*对函数进行同步,使每次传递进行的数据只能存放一个,当然如果不存放数据的话,那么将无法继续取走*/

    private Res(){}//为了保证资源的唯一性,所以使用了单例设计模式

    private static Res res =null;//懒汉式,对象的延迟加载,你懂的

    public  static Res getInstanceof () {

        /*加入多重判断提高效率*/

        if(res==null)

        /*使用同步技术,加了一个锁,保证只能有一个线程能进去*/

        synchronized(Res.class){

            /*再次判断,如果引用变量值为空,就在本类中创建本类对象*/

            if(res==null)

            res = new Res();

        }

        /*返回这个类的本类对象*/

        return res;

    }

    public  synchronized void input(String name,String user){/*接受2个参数,一个是name,一个是user*/

       while(flag){

            /*判断标记是否为true,如果为true就wait,因为里面还有数据,无法继续存放*/

            try{wait();}catch(Exception e){}

        }

        /*判断下要循环的共享数据数是否为0,为0则不进行赋值运算,不为了则进行赋值运算*/

        if(count>0){

            //把传递进行的name赋值给成员变量的name

            this.name = name;

            //把传递进来的user赋值给成员变量的user

            this.user = user;

            //为了线程的通讯,把标志改为真。

            flag = true;

            //唤醒所有正在等待的线程

            //这里如果写成notify可以吗?答案是不可以的,因为那样就可能会产生死锁。

            notifyAll();

            //打印当前线程的名称和公司和用户和共享数据还有多少个用户要存储

            System.out.println(Thread.currentThread().getName()+"...公司:"+name+"...用户..."+user+"..."+"已存入数据库...还有"+count+"客户需要存储");

        }

        else{

            /*

如果共享数据的值(也就是客户的数量)为0了,那么将虚拟机退出让程序停掉(工程被你干完了,那么就可以下班了)。

*/

            System.out.println("数据库已经满了,如果想要扩容,请付钱");/*这是我们想要的效果*/

            System.exit(0);

        }

    }

    /*对函数进行同步,跟input函数进行互斥,使每次取出数据的时候只能取走input存放的数据(当然数据只有一个)

当然如果数据不取空的话,那么将无法继续存放*/

    public  synchronized void output(){//不用接受参数,因为是拿数据嘛

        while(!flag){

            /*判断标记是否为true,如果为true就wait,因为里面没有数据,无法继续取出*/

            try{wait();}catch(Exception e){}

        }

        /*打印取出数据的线程名称和公司名称和客户和剩下多少个客户*/

        System.out.println(Thread.currentThread().getName()+"..获得的数据是...公司:" +name + "用户:"+ user+"里面还有"+count--+"个用户");

        //为了线程的互斥,把标志改为假。

        flag = false;

        /*唤醒一个正在等待的线程。因为拿东西的时候可以一个人拿而存东西的不行,只能一个一个存。*/

        notify();

    }

}

/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把资源的数据存进去,达到多线程的效果*/

class Input implements Runnable{

    /*把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类*/

    private Res r;

    /*

构造的时候,为了存取操作不错乱,这时就要保证资源的唯一性,把资源对象构造时进行参数传递进来。

*/

    Input(Res r){

        /*把资源对象的引用赋值给成员变量。*/

        this.r = r;

    }

    /*覆盖Runnable接口的run方法*/

    public  void run(){

        /*定义一个变量,使每次传递进去的资源对象数据实现切换的效果*/

        int x  =  0;

        while(true){

            /*为了程序更严谨,加入了延迟,因为客户的电脑在网络上操作的时候,有使也会卡的嘛(简单的说就是延迟吧,)*/

            try{Thread.sleep(20);}catch(Exception e){}

            /*如果变量x=0就存入"CSDN社区","CSDN——戒风"*/

            if(x==0)

            /*调用资源对象存数据的方法,传递俩个参数,一个是公司名称,一个是客户*/

            r.input("CSDN社区","CSDN——戒风");

            /*否则就存入"黑马程序员","黑马程序员——Xcc"。这样就不放真实姓名了*/

            else

            r.input("黑马程序员","黑马程序员——Xcc");

            /*对x的值进行操作,x永远只有俩个结果,一个是0,一个是1,当然这里你可以定义布尔型的值的,也是可以实现切换的效果的*/x=(x+1)%2;

        }

    }

}

/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把存进去的资源数据取出来,达到多线程的效果*/

class Output implements Runnable{

    //把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类

    private Res r;

    Output(Res r){

        /*把资源对象的引用赋值给成员变量。*/

        this.r = r;

    }

    /*覆盖Runnable接口的run方法*/

    public  void run(){

        while(true){

            try{Thread.sleep(20);}catch(Exception e){}

            /*调用资源对象取的方法,获取存入的数据*/

            r.output();

        }

    }

}

/*对工程进行测试*/

public class InputOutputTest{

    /*主函数*/

    public  static void main(String[] args){

        /*由于这个类的构造函数被私有化,如果这个类应该有提供一个获取本类对象的静态方法getInstanceof()*/

        Res r = Res.getInstanceof();

        /*创建了一个线程,并把线程要运行的代码已经封装的Runnable子类的run方法中当成参数进行传递,调用start方法,开启线程掉用线程的run方法。由于采用了多线程技术,所以就多搞几个线程,玩玩*/

        new Thread(new Input(r)).start();

        new Thread(new Output(r)).start();

        new Thread(new Input(r)).start();

        new Thread(new Output(r)).start();

    }

}



线程间通信-数据库存取JDK1.5升级版

JDK1.5中,提供了许多线程解决方案,将同步synchrozed 替换成 显示 Lock 操作

将Object 中的 wait ,notify,notifyAll,替换成了condition 对象。

该对象可以Lock锁,进行获取。

数据库存取实例更新,本方置换型对方的操作。

代码如下:

import java.util.concurrent.locks.*;

class Res{

    private String name; 

    private String user; 

    private boolean flag; 

    private int count  =  100; 

    private Lock lock = new ReentrantLock();

    private Condition condition_input = lock.newCondition();

    private Condition condition_output = lock.newCondition();

    private Res(){}

    private static Res res = null;

    public  static Res getInstanceof(){

        if(res==null)

        synchronized(Res.class){

            if(res==null)

            res = new Res();

        }

        return res;

    }

    public  void input(String name,String user){

        lock.lock();

        try{

            while(flag){

                try{condition_input.await();}catch(Exception e){}

            }

            if(count>0){

                this.name = name;

                this.user = user;

                System.out.println(Thread.currentThread().getName()+"...公司:"+name+"...用户..."+user+"..."+"已存入数据库...还有"+count+"客户需要存储");

                flag =true;

                condition_output.signalAll();

            }

            else{

                System.out.println("数据库已经满了,如果想要扩容,请付钱");

                System.exit(0);

            }

        }

        finally{

            lock.unlock();

        }

    }

    public  void output(){

        lock.lock();

        try{

            while(!flag){

                try{condition_output.await();}catch(Exception e){}

            }

            System.out.println(Thread.currentThread().getName()+"..获得的数据是...公司:" +name + "用户:"+ user+"里面还有"+count--+"个用户");

            flag = false;

            condition_input.signal();

        }

        finally{

            lock.unlock();

        }

    }

}

class Input implements Runnable{

    private Res r;

    Input(Res r){

        this.r = r;

    }

    public  void run(){

        int x=0;

        while(true){

            try{Thread.sleep(20);}catch(Exception e){}

            if(x==0)

            r.input("CSDN社区","CSDN——戒风");

            else

            r.input("黑马程序员","黑马程序员——Xcc");

            x=(x+1)%2;

        }

    }

}

class Output implements Runnable{

    private Res r;

    Output(Res r){

        this.r = r;

    }

    public  void run(){

        while(true){

            try{Thread.sleep(20);}catch(Exception e){}

            r.output();

        }

    }

}

public class InputOutputTest{

    public  static void main(String[] args){

        Res r = Res.getInstanceof();

        new Thread(new Input(r)).start();

       new Thread(new Output(r)).start();

        new Thread(new Input(r)).start();

        new Thread(new Output(r)).start();

    }

}


思考一下:单例设计模式的synchroized替换吗?为什么?

答案:想都不用想,直接回答no,因为如果替换起来比较麻烦,Java不是还保留synchronized关键字吗?目的简化了我们的书写,应该就是因为这个原因。

当然我不知道我的答案是正确还是错误。这只不过是我的直观想法。



多线程(停止线程)

stop() 方法已经过时,如何停止线程?

只有一种,run()方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run()方法结束,也就是线程结束。

特殊情况:当线程处于冻结状态(不是运行状态),就不会读取到标记,线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态,这样就可以操作标记让线程结束,Thread类提供该方法interrupt();


注:线程对象.interrupt(); 强制让冻结状态的线程恢复到运行状态,线程对象冻结的地方会抛出InterruptedException异常,处理该异常时(catch){改变标记},来停止线程。

class StopThread implements Runnable{

    private boolean flag  =true; //定义判断标记,让线程结束.

    public  synchronized void run(){

        while(flag){

            try{wait();}

            catch(InterruptedException e){

                System.out.println(Thread.currentThread().getName()+"……Exception");

                //flag =false; //当发生异常,说明调用了interrupt方法强制线程清楚冻结,希望线程停

                changFlag();

            }

            System.out.println(Thread.currentThread().getName()+"……run");

        }

    }

    public  void changFlag(){

        flag = false;

    }

}

public class StopDemo{

    public  static void main(String[]args){

        StopThread st = new StopThread();

       Thread t1 = new Thread (st);

        Thread t2 = new Thread (st);

        t1.start();

        t2.start();

        int num = 0;

        while(true){

            if(num++ == 60){

                t1.interrupt();//让冻结的线程恢复到运行状态

                t2.interrupt();

                break;

            }

            System.out.println(Thread.currentThread().getName()+"……"+num);

        }

        System.out.println("over");

    }

}


多线程(守护线程)(后台线程)

线程对象.setDaemon(true);将该线程标记为守护线程或用户线程。 

当正在运行的线程都是守护线程时,Java虚拟机退出,程序结束。

注意:一个线程被标示为守护线程,并不是他不运行了,他跟其他线程一样运行。只是当前台线程执行完后,这些线程不会结束。

09-多线程(Join方法)

Join():当A线程执行到了B线程的.Join()方法时,A线程就会让出CPU执行权,等待直到B线程执行完,A才会执行。

Join可以用来临时加入线程执行。

注意:碰见了谁的Join()方法,就只等谁。等他结束就执行。

线程对象.start();

线程对象.join(); 

t1.start();

t1.join(); //等待t1执行完,才继续执行主线程和其他线程,其他线程在期间是冻结的

t2.start();

——————————————————

t1.start();

t2.start();

t1.join(); //当主线程执行到这里,他会冻结,但是t2不会冻结,他和t1交替执行,主线程等t1执行完了才执行,不管t2有没有执行完。


10-多线程(优先级&yield方法)


优先级:线程对象.setPriority(intnewPriority);设置线程优先权,1-10,默认5,越大,抢到CPU执行权机会越大;

(Thread.MAX_PRIORITY, MIN_PRIORITY,NORM_PRIORITY)