Java学习笔记(多线程_2)

15 多线程

15.3 多线程间通信

15.3.1 线程间通信方法

多个线程在操作统一资源,但操作的动作不同。

等待唤醒机制:
例1:

    class Res{
        String name;
        String sex;
        boolean flag = false;
    }

    class Input implements Runnable{
        Res r = new Res();
        Input(Res r){
            this.r = r;
        }
        public void run(){  

            int x = 0;  
            while(true){
                synchronized(r){//输入和输出采用同一个锁
                    if(r.flag){
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if(x==0){
                        r.name = "李雷"; 
                        r.sex = "男";
                    }else{
                        r.name = "韩梅梅";
                        r.sex = "女";
                    }
                    x = (x+1)%2;
                    r.flag = true;
                    r.notify();
                }
            }
        }
    }

    class Output implements Runnable{
        Res r = new Res();
        Output(Res r){
            this.r = r;
        }
        public void run() {
            while(true){
                synchronized(r){
                    if(!r.flag){
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(r.name+"..."+r.sex);
                    r.flag= false;
                    r.notify();
                }
            }
        }

    }

    public class InputOutputDemo {
        public static void main(String[] args) {
            Res r = new Res();
            Input in = new Input(r);
            Output out = new Output(r);

            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);

            t1.start();
            t2.start();
        }
    }

运行结果:

上述代码中输入一个输出一个的机理:等待唤醒机制。

多线程加了同步依然不安全需检查是否满足以下三个条件:

  1. 是否存在两个以上线程;
  2. 两个线程是否均同步;
  3. 是否应用同一个锁,必须用同一锁。

wait()、notify()和notifyAll()都是用在同步中,因为都需要持有锁(监视器monitor)的线程操作,而只有同步中才有锁。

等待和唤醒必须用同一个锁,锁可以用任意Object对象,所以可以被任意对象调用的方法定义Object类中。

例2(例1优化):

class Res{
    private String name;
    private String sex;
    private boolean flag = false;

    public synchronized void set(String name,String sex){
        if(flag)
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        this.name = name;
        this.sex = sex;
        this.flag = true;
        this.notify();
    }

    public synchronized void out(){
        if(!this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name+"..."+sex);
        this.flag= false;
        this.notify();

    }
}

class Input implements Runnable{
    Res r = new Res();
    Input(Res r){
        this.r = r;
    }
    public void run(){          
        int x = 0;  
        while(true){                
            if(x==0){
                r.set("李雷", "男");               
            }else{
                r.set("韩梅梅", "女"); 
            }
            x = (x+1)%2;                
        }
    }
}

class Output implements Runnable{
    Res r = new Res();
    Output(Res r){
        this.r = r;
    }
    public void run() {
        while(true){
            r.out();
        }
    }

}

public class InputOutputDemo {
    public static void main(String[] args) {
        Res r = new Res();

        Thread t1 = new Thread(new Input(r));
        Thread t2 = new Thread(new Output(r));

        t1.start();
        t2.start();
    }
}

两个对象多个线程问题:

class Res{
    private String name;
    private String sex;
    private boolean flag = false;
    private int count = 1;

    public synchronized void set(String name,String sex){
        while(flag)
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        this.name = name+count;
        this.sex = sex;
        count++;
        System.out.println("设置"+this.name+"..."+sex);
        this.flag = true;
        this.notifyAll();//需全部唤醒,否则按等待顺序下一个还是set,会覆盖之前的set
    }

    public synchronized void out(){
        while(!this.flag){//由于存在多个线程,每次拿到锁都必须判断
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("输出..."+name+"..."+sex);
        this.flag= false;
        this.notifyAll();

    }
}

class Input implements Runnable{
    Res r = new Res();
    Input(Res r){
        this.r = r;
    }
    public void run(){          
        int x = 0;  
        while(true){                
            if(x==0){
                r.set("李雷", "男");               
            }else{
                r.set("韩梅梅", "女"); 
            }
            x = (x+1)%2;                
        }
    }
}

class Output implements Runnable{
    Res r = new Res();
    Output(Res r){
        this.r = r;
    }
    public void run() {
        while(true){
            r.out();
        }
    }

}

public class InputOutputDemo {
    public static void main(String[] args) {
        Res r = new Res();

        Thread t1 = new Thread(new Input(r));
        Thread t2 = new Thread(new Input(r));
        Thread t3 = new Thread(new Output(r));
        Thread t4 = new Thread(new Output(r));

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

运行结果:

注意:
while判断和notify会导致死锁出现。

15.3.2 JDK1.5特性

Jdk1.5中将同步和锁封装为对象,并将操作锁的隐式方式定义到该对象中,将隐式动作变为显示动作。

同步中synchronized替换为了Lock,Condition 替代了 Object 监视器方法的使用。

Lock接口可以在一个锁上加上多组监视器。

Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。

import java.util.concurrent.locks.*;

class Res{
        private String name;
        private String sex;
        private boolean flag = false;
        private int count = 1;
        //创建一个锁对象
        private Lock lock = new ReentrantLock();
        //通过已有锁获取两组监视器,一组监视设置函数,一组监视输出函数
        private Condition con_set = lock.newCondition();
        private Condition con_out = lock.newCondition();

        public void set(String name,String sex) throws InterruptedException{
            lock.lock();
            try{
            while(flag)
                    con_set.await();        
                    this.name = name+count++;
                    this.sex = sex;

                    System.out.println("设置"+this.name+"..."+sex);
                    flag = true;
                    con_out.signalAll();
            }finally{

                    lock.unlock();
                }
        }

        public void out() throws InterruptedException{
            lock.lock();
            try{
                while(!flag){//由于存在多个线程,每次拿到锁都必须判断
                    con_out.await();        
                }
                    System.out.println("输出..."+name+"..."+sex);
                    flag= false;
                    con_set.signalAll();
            }finally{
                    lock.unlock();
                }
            }
        }

    class Input implements Runnable{
        Res r = new Res();
        Input(Res r){
            this.r = r;
        }
        public void run(){          
            int x = 0;  
            while(true){                
                if(x==0){
                    try {
                        r.set("李雷", "男");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }               
                }else{
                    try {
                        r.set("韩梅梅", "女");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } 
                }
                x = (x+1)%2;                
            }
        }
    }

    class Output implements Runnable{
        Res r = new Res();
        Output(Res r){
            this.r = r;
        }
        public void run() {
            while(true){
                try {
                    r.out();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }       
    }

    public class InputOutputDemo {
        public static void main(String[] args) {
            Res r = new Res();

            Input in = new Input(r);
            Output out = new Output(r);

            Thread t1 = new Thread(in);
            Thread t2 = new Thread(in);
            Thread t3 = new Thread(out);
            Thread t4 = new Thread(out);

            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }

运行结果:

15.3.3 线程停止

开启多线程通常为循环结构,只要控制循环,就可以让run方法结束,即线程结束。

特殊情况:
线程处于冻结状态,无法读取标记,程序无法结束。

可以使用Thread中的interrupt()方法将线程从冻结强制恢复至运行状态,按api中的描述:

“如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。”

此时会抛出InterruptedException异常,则进行相应处理。

public class StopThreadDemo {
    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");
    }
}

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;
            }
            System.out.println(Thread.currentThread().getName()+"..run");
        }
    }
    public void changeFlag(){
        flag = false;
    }
}

运行结果:

15.3.4 其他方法

setDaemon()方法:

public class StopThreadDemo {
    public static void main(String[] args) {
        StopThread st = new StopThread();

        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.setDaemon(true);
        t2.setDaemon(true);
        t1.start();
        t2.start();

        int num = 0;

        while(true){
            if(num++ == 60){

                break;
            }
            System.out.println(Thread.currentThread().getName()+"..."+num);
        }
        System.out.println("over");
    }
}

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;
            }
            System.out.println(Thread.currentThread().getName()+"..run");
        }
    }
}

运行结果:

ps:作为守护线程,主线程停止,守护线程也停止。

yield方法

public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。

setPriority方法

public final void setPriority(int newPriority)
更改线程的优先级。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。

在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。

你可能感兴趣的:(java,多线程,通信,基础)