3.2多线程(线程通信)

1.线程间通讯:
多个线程在处理同一资源,但是任务却不同。

2.等待唤醒机制:
涉及的方法:

1.wait()  让线程处于冻结状态,被wait的线程会被存储到线程池。通过锁来区分线程池(等待集)
2.notify()  唤醒线程池中的一个线程 (任意)。处于运行或者临时阻塞状态,即获得执行资格
3.notifyAll()   唤醒线程池中的所有线程。

这些方法必须定义在同步中,
因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait,notify,notifyall 定义在了object类中?

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

图解:

等待唤醒机制图解

代码:

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


class Input implements Runnable
{
    private Resource r;

    Input(Resource r)
    {
        this.r = r;
    }

    public void run()
    {
        int x = 0;

        while(true)
        {
            synchronized(r)
                {
                   x = ++x%2;

                    if(r.flag)
                        try
                        {
                            r.wait();
                        }
                        catch (Exception e)
                        {
                        }


                    if(x == 0)
                    {
                        r.name = "小美";
                        r.sex = "女";
                    }
                    else
                    {
                        r.name = "王五";
                        r.sex = "男";
                    }
                    r.flag = true;  
                    r.notify();
                }

        }       
    }
}



class Output implements Runnable
{   
    private Resource r;

    Output(Resource r)
    {
        this.r = r;
    }


    public void run()
    {
        while(true)
        {

                synchronized(r)
                {
                    if(!r.flag)
                        try
                        {
                            r.wait();   //说明是哪个锁调用wait()。对象锁也称对象监视器,而wait就是操作对象监视器上的线程。
                        }
                        catch (Exception e)
                        {
                        }

                    if(r!=null) 
                        System.out.println(r.name + "....." + r.sex);   

                        r.flag = false;
                        r.notify();  //说明是哪个锁调用notify()
                    }
                }
        }
    }



class  ResourceDemo
{
    public static void main(String[] args) 
    {
        Resource r = new Resource();   //c创建资源
        Input in = new Input(r);    //创建人物
        Output out = new Output(r);   

        Thread t1 = new Thread(in);    //创建线程,执行路径
        Thread t2 = new Thread(out);

        t1.start();      //开启线程
        t2.start();
    }
}

3.等待唤醒 代码 开发优化:

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

    public synchronized  void set(String name, String sex)   //已经解决name,sex同步问题
    {
            if(flag)
                    try
                        {
                            this.wait();
                        }
                        catch (Exception e)
                        {
                        }
            this.name = name;    
            this.sex =sex;
            flag = true;
            notify();
    }

    public synchronized void out()
    {
                if(!flag)
                    try
                    {
                        wait(); 
                    }
                    catch (Exception e)
                    {
                    }

                if(this!=null)  
                    System.out.println(name + "....." + sex);   
                flag = false;
                notify();
    }
}


class Input implements Runnable
{
    private Resource r;

    Input(Resource r)
    {
        this.r = r;
    }

    public void run()
    {
        int x = 0;

        while(true)
        {

                if(x == 0)
                {
                        r.set("xiaoni","女");
                }
                else
                {
                        r.set("王五","男");
                }   

                x = ++x%2;
    }
    }
}



class Output implements Runnable
{   
    private Resource r;

    Output(Resource r)
    {
        this.r = r;
    }


    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}



class  ResourceDemo
{
    public static void main(String[] args) 
    {
        Resource r = new Resource();   //c创建资源
        Input in = new Input(r);    //创建人物
        Output out = new Output(r);   

        Thread t1 = new Thread(in);    //创建线程,执行路径
        Thread t2 = new Thread(out);

        t1.start();      //开启线程
        t2.start();
    }
}

4.多生产者多消费者 问题:

两个关键点:
1.判断标记的循环:
应该用while,始终判断标记。但是可能会造成全部等待,产生死锁。

2.所以,为了避免死锁,应该使用全部唤醒notifyall(),这样可以唤醒对方线程,解决死锁问题。

if判断标记,只有一次,会导致不该运行的线程运行了。出现数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行。
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll:解决了,本方线程一定会唤醒对方线程的问题。但是全部唤醒效率低。

JDK1.5新工具解决办法:接口 Lock,接口Condition 

接口 Lock:
它替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。同时更为灵活。可以一个锁上加上多组监视器。
lock() : 获取锁
unlock() : 释放锁,通常定义在finally代码块中。

Lock lock = new ReentrantLock(); //互斥锁
void show()
{
    lock.lock();
    try
    {
    code...
    }
    finally
    {
    lock.unlock();  //释放锁一定要做
    }   
}

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

API:
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

JDK1.5更新图解:
这里写图片描述

JDK1.5之前实现代码:

/*
生产者,消费者

多生产者,多消费者
*/
class Resource
{
    private String name;
    private int count = 1;
    private boolean flag = false;

    public synchronized void set(String name)
    {
        while(flag)         //if(flag)          //会发生死锁                                                 
                                                                    //解决方案:全部唤醒, notifyAll();
            try
            {
                wait();       //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。所以有可能线程跳过了判断继续执行。
                                                        //解决方案将if 改成while,但可能导致四个线程全部等待,发生死锁
            }
            catch (InterruptedException e)
            {
            }

        this.name = name + count;
        System.out.println(Thread.currentThread().getName() + "生产了 " + this.name);
        count++;
        flag = true;
        notifyAll();
    }


    public synchronized void out()
    {
            while(!flag)        //if(!flag)        //解决方案改成while    
            try
                {
                    wait();          //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。
                }
                catch (InterruptedException e)
                {
                }

            System.out.println(Thread.currentThread().getName() +"吃掉了 " + name);
            flag = false;
            notifyAll();
    }

}


class Producer implements Runnable
{
    Resource r;
    Producer(Resource r)
    {
        this.r = r;
    }

    public void run()
    {
            while(true)
        {
            r.set("烤鸭");
        }
    }
}

class Consumer implements Runnable
{
    Resource r;
    Consumer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}

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

        Producer p = new Producer(r);
        Consumer c = new Consumer(r);

        Thread t1 = new Thread(p,"生产者1");
        Thread t2 = new Thread(p,"生产者2");
        Thread t3 = new Thread(c,"消费者1");
        Thread t4 = new Thread(c,"消费者2");

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

JDK1.5之 后 实现代码:

/*
生产者,消费者

多生产者,多消费者

jdk1.5后的解决方案:

*/
import java.util.concurrent.locks.*;   //导入lock包

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

    Lock lock =new  ReentrantLock();   //创建一个锁对象

    Condition producer_con = lock.newCondition();  //通过已有的锁,获得该锁上的监视器对象
    Condition consumer_con = lock.newCondition();

    public  void set(String name)
    {
        lock.lock();    // 获取锁
        try
        {
                    while(flag)         //if(flag)          //会发生死锁                                                 
                                                                            //解决方案:全部唤醒, notifyAll();
                    try
                    {
                        producer_con.await();       //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。所以有可能线程跳过了判断继续执行。
                                                                //解决方案将if 改成while,但可能导致四个线程全部等待,发生死锁
                    }
                    catch (InterruptedException e)
                    {
                    }

                this.name = name + count;
                System.out.println(Thread.currentThread().getName() + "生产了 " + this.name);
                count++;
                flag = true;
                consumer_con.signal();
        }
        finally
        {
            lock.unlock();  //释放锁
        }

    }


    public  void out()
    {
            lock.lock();    // 获取锁
            try
            {
                            while(!flag)        //if(!flag)        //解决方案改成while    
                                try
                                    {
                                        consumer_con.await();          //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。
                                    }
                                    catch (InterruptedException e)
                                    {
                                    }

                                System.out.println(Thread.currentThread().getName() +"吃掉了 " + name);
                                flag = false;
                                producer_con.signal();
            }
            finally
            {
                lock.unlock();  //释放锁
            }

    }

}


class Producer implements Runnable
{
    Resource r;
    Producer(Resource r)
    {
        this.r = r;
    }

    public void run()
    {
            while(true)
        {
            r.set("烤鸭");
        }
    }
}

class Consumer implements Runnable
{
    Resource r;
    Consumer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}

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

        Producer p = new Producer(r);
        Consumer c = new Consumer(r);

        Thread t1 = new Thread(p,"生产者1");
        Thread t2 = new Thread(p,"生产者2");
        Thread t3 = new Thread(c,"消费者1");
        Thread t4 = new Thread(c,"消费者2");

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

API范例 补充:

 import java.util.concurrent.locks.*;

 class BoundedBuffer {

   final Lock lock = new ReentrantLock();

   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;


   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();

       items[putptr] = x; 

        System.out.println(Thread.currentThread().getName() + " produced " +   putptr);
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }


   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();

       Object x = items[takeptr]; 

        System.out.println(Thread.currentThread().getName() + " consum " + takeptr);
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }

class Producer implements Runnable
{
    BoundedBuffer b;

    Producer(BoundedBuffer b)
    {
        this.b = b;
    }

    public void run()
    {
        while(true)
        {
            try
            {
                b.put(new Object());
            }
            catch (Exception e)
            {
            }       
        }
    }
}

class Consumer implements Runnable
{
    BoundedBuffer b;

    Consumer(BoundedBuffer b)
    {
        this.b = b;
    }

    public void run()
    {
        while(true)
        {   
            try
            {
                b.take();
            }
            catch (Exception e)
            {
            }

        }
    }
}



class ProducerConsumerAPIDemo 
{
    public static void main(String[] args) 
    {
        BoundedBuffer boun = new BoundedBuffer();

        Producer pro = new Producer(boun);
        Consumer cons = new Consumer(boun);

        Thread t1 = new Thread(pro,"Producer1");
        Thread t2 = new Thread(pro,"Producer2");
        Thread t3 = new Thread(cons,"Consumer1");
        Thread t4 = new Thread(cons,"Consumer2");
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

谁拿锁谁执行。

5.wait 和 sleep的区别:

1.wait可以指定时间也可以不指定。
  sleep必须指定时间
2.在同步中时,对于CPU的执行权和锁的处理不同。
wait: 释放执行权,释放锁
sleep: 释放执行权,不释放锁

6.停止线程:

1.stop方法:过时
2.run方法结束:标记法
    任务中都会有循环结构,只要控制住循环就可以结束任务。
    控制循环通常就用定义标记来完成。
3.线程处于了冻结状态,无法读取标记。
public synchronized void run()
{
    while(flag)
    {
        wait();
        ...
    }
}
Thread方法:interrupt()中断线程。会抛出异常。

可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。强制动作会发生冻结状态异常InterruptedException,记得要处理。然后给标记赋值,让其强制读取。结束线程。

public synchronized void run()
{
    while(flag)
    {
        try
        {
            wait();
        }
        catch(InterruptedException e)
        {}
        flag =flase;
    }
}
t1.interrupt();   //强制唤醒

标记法示例:

class StopThread implements Runnable
    {
        private boolean flag = true;
        public void run()
        {
                while(flag)
                {
                    System.out.println(Thread.currentThread().getName()+".....");
                }
        }

        public void setFlag()
        {
            flag = false;
        }
    }

    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 = 1;
            for(;;)
            {
                if(++num==50)
                {
                    st.setFlag();
                    break;
                }
                System.out.println("main......"  + num);
            }
            System.out.println("over");
        }
    }

7.守护线程函数 setDaemon():将线程设置为后台线程(守护线程,用户线程等)。
前台线程必须手动结束。
当前台线程全部结束后,后台线程会自动结束。
t1.setDaemon(true);

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

8.其他方法:

1.join(): 等待该线程终止。 throws InterruptedException
先让该线程运行完到终止。也可以让冻结的线程强制恢复(interrupt()方法的功能)。
t1.join();//t1线程要申请加入进来,运行。这时主线程会将执行权释放出来,执行资格也释放,处于冻结状态。等t1结束后主线程再执行。

临时加入一个线程运算时可以使用join方法。

2.toString(): 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。  例:Thread[Thread-0,5,main] 

优先级: 获取cpu执行权的几率,范围110。其中关键的几个优先级进行了字段封装:静态常量
MAX_PRIORITY 线程可以具有的最高优先级。10
MIN_PRIORITY 线程可以具有的最低优先级。1
NORM_PRIORITY 分配给线程的默认优先级。5

线程组:操作一个组的线程。方便。

3.setPriority():更改线程的优先级。
例:  //t2.setPriority(10);
t2.setPriority(Thread.MAX_PRIORITY) //cpu会稍微优先照顾一下

4.Thread.yield():线程临时暂停,释放执行权。给其他线程机会,自己同时也有机会。

9.快速创建线程:

//线程的子类对象,匿名内部类
class ThreadTest
{
    public static void main(String[] args)
    {
        new Thread()        //线程一:直接创建Thread子类对象
        {
            public void run()
            {
                for(int x=0; x<50; x++)
                {
                    System.out.println(Thread.currentThread().getName()+ "......x= " + x);
                }
            }
        }.start();

        new Thread(new Runnable()   //线程二:创建Runnable接口的子类对象封装方法,把方法对象传给Theard线程对象       
        {
            public void run()
            {
                for(int x=0; x<50; x++)
                {
                    System.out.println(Thread.currentThread().getName()+ "...........y= " + x);
                }
            }
        }).start();

        for(int x=0; x<50; x++)   //线程三:主函数
        {
            System.out.println(Thread.currentThread().getName()+  "...z= " + x);
        }
    }
}

10.多线程面试题:

1.class Test implements Runnable
{
    public void run(Thread t)   //这是子类的特有方法,并没有实现接口Runnable
的run()方法,所以该类要么是抽象类,要么就要覆盖run方法。显然没有定义抽象类,所以会报错。
    {}
}

2.class ThreadTest
{
    public static void main(String[] args)
    {
        new Thread   //没有任务对象,以线程类本身为主
        (
            new Runnable()   //没有子类,以任务对象为主
            {
                public void run()
                {
                    System.out.println("runnable run");
                }
            }
        )
        {  //以子类为主
            public void run()
            {
                System.out.println("subThread run");
            }
        }.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值