zoukankan      html  css  js  c++  java
  • Java:多线程<三>死锁、线程间通讯

    死锁:

    同步嵌套同步,而且使用的锁不是同一把锁时就可能出现死锁

    class Test implements Runnable
    {
        private boolean flag;
        Test(boolean flag)
        {
            this.flag = flag;
        }
    
        public void run()
        {
            if(flag)
            {
                while(true)
                {
                    synchronized(MyLock.locka)
                    {
                        System.out.println(Thread.currentThread().getName()+"...if locka ");
                        synchronized(MyLock.lockb)
                        {
                            System.out.println(Thread.currentThread().getName()+"..if lockb");                    
                        }
                    }
                }
            }
            else
            {
                while(true)
                {
                    synchronized(MyLock.lockb)
                    {
                        System.out.println(Thread.currentThread().getName()+"..else lockb");
                        synchronized(MyLock.locka)
                        {
                            System.out.println(Thread.currentThread().getName()+".....else locka");
                        }
                    }
                }
            }
        }
    }
    
    
    class MyLock
    {
        static Object locka = new Object();
        static Object lockb = new Object();
    }
    
    class  DeadLockTest
    {
        public static void main(String[] args) 
        {
            Thread t1 = new Thread(new Test(true));
            Thread t2 = new Thread(new Test(false));
            t1.start();
            t2.start();
        }
    }

    线程间通讯:

    其实就是多线程操作同一个资源,但是操作的动作不同。

    例,用多线程操作生成一个人名+性别(男的用英文表示,女的用中文表示),然后打印。

    class Res
    {
        String name;
        String sex;
    }
    
    class Input implements Runnable
    {
        private Res r;
        Input(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            int x = 0;
            while(true)
            {
                synchronized(r)
                {
                    if (x==0)
                    {
                        r.name = "Mike";
                        r.sex="man";
                    }
                    else
                    {
                        r.name = "丽丽";
                        r.sex="女";
                    }
                }
                x = (x+1)%2;
            }
        }
    }
    
    class Output implements Runnable
    {
        private Res r;
        Output(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {
                synchronized(r)
                {
                    System.out.println(r.name + "..." +r.sex);
                }            
            }
        }
    }
    
    class  ThreadCommunication
    {
        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,使用的锁在内存中一定要保证是唯一的;

    在上个例子中,并不是按照我们期待的那样,生成一个对象然后打印该对象,即输入一个就输出一个,这就需要用到等待-唤醒机制。

    等待-唤醒:

    wait:

    notify:

    notifyall:

    都使用在同步中,因为要对持有监视器(锁)的线程操作,所以使用在同步中,因为只有同步才具有锁。

    为什么这些操作线程的方法,要定义在Object类中呢?以为你这些方法在操作同步中线程时,都必须要表示它们所操作线程持有的锁。只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒必须是同一个锁。而锁可以是任意对象,所以被任意对象调用的方法定义在Object中。

    class Res
    {
        String name;
        String sex;
        Boolean flag=false;
    }
    
    class Input implements Runnable
    {
        private Res r;
        Input(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            int x = 0;
            while(true)
            {
                synchronized(r)
                {
                    if(r.flag)
                    {
                        try
                        {
                            r.wait();//如果flag=ture,说明资源中已经有了生产的对象,那么不再生产了,生产线程就wait
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                    if (x==0)
                    {
                        r.name = "Mike";
                        r.sex="man";                
                    }
                    else
                    {
                        r.name = "丽丽";
                        r.sex="女";
                    }
                    r.flag = true;//改变flag标记
                    r.notify();//唤醒线程池中的第一个等待线程,只在本程序中就是out
                }
                x = (x+1)%2;
                
            }
        }
    }
    
    class Output implements Runnable
    {
        private Res r;
        Output(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {            
                synchronized(r)
                {    
                    if(!r.flag)
                    {
                        try
                        {
                            r.wait();//如果资源中没有生产对象,那么就没有可以打印的对象,这是输出就wait
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                        
                    System.out.println(r.name + "..." +r.sex);
                    r.flag = false;//改变标记
                    r.notify();//唤醒线程池中的第一个等待线程,本程序中即in
                }            
            }
        }
    }
    
    class  ThreadCommunication
    {
        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();
        }
    }

    优化代码

    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 (Exception e)
                {
                    e.printStackTrace();
                }
                this.name = name;
                this.sex = sex;
                flag = ture;
                this.notify();
            }
            
        }
        public synchronized void out()
        {
            if (!flag)
            {
                try
                {
                    this.wait();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                System.out.println(name + "..." + sex);
                flag = false;
                this.notify();
            }        
        }
    }
    
    class Input implements Runnable
    {
        private Res r;
        Input(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            int x = 0;
            while(true)
            {
                synchronized(r)
                {
                    if (x==0)
                    {
                        r.set("Mike","man");                
                    }
                    else
                    {
                        r.set("丽丽","女");
                    }
                }
                x = (x+1)%2;            
            }
        }
    }
    
    class Output implements Runnable
    {
        private Res r;
        Output(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {            
                synchronized(r)
                {    
                    r.out();
                }            
            }
        }
    }
    
    class  ThreadCommunication
    {
        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();
        }
    }

    当多个线程操作同一个资源,且同一个操作也有多个线程时,就需要使用while判断标记,并用notifyAll唤醒所有线程。

    class FactoryDemo 
    {
        public static void main(String[] args) 
        {
            Resource r = new Resource();
            Product pro = new Product(r);
            Consumer con = new Consumer(r);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    class Product implements Runnable
    {
        private Resource r = new Resource();
        Product(Resource r)
        {
            this.r = r;
        }
        public void run()
        {
            while (true)
            {
                r.set("商品");
            }        
        }
    }
    
    class Consumer implements Runnable
    {
        private Resource r = new Resource();
        Consumer(Resource r)
        {
            this.r = r;
        }
        public void run()
        {
            while (true)
            {
                r.out();
            }        
        }
    }
    
    class Resource
    {
        private String name;
        private int count = 1;
        private boolean flag = false;
        public synchronized void set(String name)
        {
            while (flag)//使用while判断标记,避免线程在判断标记之后醒了不判断标记
            {
                try
                {
                    this.wait();
                }
                catch (Exception e)
                {
                }
            }
            this.name = name + "--" + count++;
            System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
            flag = true;
            this.notifyAll();
        }
        public synchronized void out()
        {
            while (!flag)//使用while判断标记,避免线程在判断标记之后醒了不从新判断标记
            {
                try
                {
                    wait();
                }
                catch (Exception e)
                {
                }
            }
            System.out.println(Thread.currentThread().getName()+"...消费者.................."+this.name);
            flag = false;
            this.notifyAll();
        }
    }

    但notifyAll唤醒对方线程的同时也把本方的线程唤醒了,这是我们不希望看到的。对于该问题的优化,参见Lock接口

  • 相关阅读:
    ASP.NET安全问题-- 创建安全的Web应用程序
    浅谈ASP.NET内部机制(八)
    ASP.NET 配置文件纵横谈(一)
    项目开发-让设计模式成为一种心智
    浅谈ASP.NET内部机制(七)
    ASP.NET 配置文件纵横谈(二)
    GridView的分页是否真的是鸡肋呢?
    SQL开发中容易忽视的一些小地方(四)
    SQL开发中容易忽视的一些小地方( 三)
    怎样才能充分利用SQL索引
  • 原文地址:https://www.cnblogs.com/siyingcheng/p/4320915.html
Copyright © 2011-2022 走看看