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