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接口

原文地址:https://www.cnblogs.com/siyingcheng/p/4320915.html