互斥锁
1.共享“基本数据类型的资源”(多窗口买票)
1)共享静态的“基本数据类型的资源”
/*** 2018年5月1日 上午9:40:31* * @author <a href="mailto:447441478@qq.com">宋进宇</a> 买票*/
public class SaleTicketRun implements Runnable {//如果共享的资源 不是基本数据类型的话,就用资源本身来当锁,//否则的话就制造一个平行的对象来当锁。private static int TicketCount = 200;// 共享的资源//制造一个与资源平行的对象锁private static Object objLock = new Object();@Overridepublic void run() {while (true) {synchronized (objLock) {//如果TicketCount大于0说明可以卖票if (TicketCount>0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + TicketCount + "张票");TicketCount--;}else{break;}}}}}
/*** 2018年5月1日 上午9:35:31* @author <a href="mailto:447441478@qq.com">宋进宇</a>* 共享静态资源演示*/
public class ShareStaticResource {public static void main(String[] args) {Thread t1 = new Thread(new SaleTicketRun());t1.setName("1号售票员");Thread t2 = new Thread(new SaleTicketRun());t2.setName("2号售票员");Thread t3 = new Thread(new SaleTicketRun());t3.setName("3号售票员");Thread t4 = new Thread(new SaleTicketRun());t4.setName("4号售票员");t1.start();t2.start();t3.start();t4.start();}
}
2)共享非静态的“基本数据类型的资源”
/*** 2018年5月1日 上午9:40:31* @author <a href="mailto:447441478@qq.com">宋进宇</a> 买票*/
public class SaleTicketRun implements Runnable {//如果共享的资源 不是基本数据类型的话,就用资源本身来当锁,//否则的话就制造一个平行的对象来当锁。private int TicketCount = 200;// 共享的资源//不难发现,当共享的资源是非静态时,当前对象(this)就是与资源平行的对象//制造一个与资源平行的对象锁//private Object objLock = new Object();@Overridepublic void run() {while (true) {//synchronized (objLock) {synchronized (this) {//如果TicketCount大于0说明可以卖票if (TicketCount>0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + TicketCount + "张票");TicketCount--;}else{break;}}}}}
/*** 2018年5月1日 上午10:13:40* @author <a href="mailto:447441478@qq.com">宋进宇</a>* 共享非静态资源演示*/
public class ShareNoStaticResource {public static void main(String[] args) {SaleTicketRun saleTicketRun = new SaleTicketRun();//共享非静态资源就必须保持是同一份资源,即Runnable 要同一个//否则就没有法实现共享的效果Thread t1 = new Thread(saleTicketRun);t1.setName("1号售票员");Thread t2 = new Thread(saleTicketRun);t2.setName("2号售票员");Thread t3 = new Thread(saleTicketRun);t3.setName("3号售票员");Thread t4 = new Thread(saleTicketRun);t4.setName("4号售票员");t1.start();t2.start();t3.start();t4.start();}
}
2.共享栈
/*** 2018年5月1日 上午10:24:48* @author <a href="mailto:447441478@qq.com">宋进宇</a> * 共享栈:* 多线程共享一个“栈” 只有 信号(signal) 为 true 才能 放(push)* 只有 信号(signal) 为 false 才能 取(pop)*/
public class MyStack {private char[] chs = new char[6];private int index = 0;//栈顶的位置private boolean signal = true;/** 这里不做越界判断*/public synchronized void push(char c) {while(!signal) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}chs[index] = c;index++;signal = false;notifyAll();}/** 这里不做越界判断*/public synchronized char pop() {while(signal) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}index--;System.out.println(chs[index]);signal = true;notifyAll();return chs[index];}
}
/*** 2018年5月1日 上午10:33:20* @author <a href="mailto:447441478@qq.com">宋进宇</a>* 演示共享栈*/
public class Demo {public static void main(String[] args) {MyStack myStack = new MyStack();Demo demo = new Demo();Thread pushThread = demo.new PushThread(myStack);Thread pushThread2 = demo.new PushThread(myStack);Thread popThread = demo.new PopThread(myStack);Thread popThread2 = demo.new PopThread(myStack);popThread2.start();popThread.start();pushThread2.start();pushThread.start();}class PushThread extends Thread {MyStack myStack = null;public PushThread(MyStack myStack) {this.myStack = myStack;}@Overridepublic void run() {for (int i = 97; i < 103; i++) {myStack.push((char)i);}}}private class PopThread extends Thread {MyStack myStack = null;public PopThread(MyStack myStack) {this.myStack = myStack;}@Overridepublic void run() {for (int i = 97; i < 103; i++) {myStack.pop();}}}
}
死锁
死法1:
多个线程共用同一个对象锁,互相等待。
/*** 2018年5月2日 下午3:22:39* @author <a href="mailto:447441478@qq.com">宋进宇</a>* 这种死锁是只有当线程 b 中 synchronized块还没执行时,* a 线程调用了 b.join()方法,把 b 线程并入到 a 线程 * 此时 b线程内的还未执行的代码就在a线程中执行,但是 b 线程要执行* synchronized块中的代码需要拿到 锁 才能执行,可是锁在 a线程 手上,* a 还没释放 锁 , 导致出现 死锁*/
public class Demo {public static void main(String[] args) {Source s = new Source();Thread b = new Thread( new ThreadB( s ));Thread a = new Thread( new ThreadA( s, b ) );b.start();a.start();}
}
class Source{public int num = 666;
}
class ThreadA implements Runnable{private Source s = null;private Thread b;public ThreadA( Source s, Thread b ) {super();this.s = s;this.b = b;}@Overridepublic void run() {System.out.println( "ThreadA 进来了" );synchronized ( s ) {try {b.join();} catch (InterruptedException e) {}System.out.println( s.num );}}
}
class ThreadB implements Runnable{private Source s = null;public ThreadB( Source s ) {super();this.s = s;}@Overridepublic void run() {System.out.println( "ThreadB 进来了" );synchronized ( s ) {System.out.println( s.num );}}
}
死法2:
互相持有对方所需的资源(即每个线程都需要同时拿到多个资源才能继续执行,而多个线程都处于:各持有一部分,在等待另一部分。)
/*** 2018年5月2日 下午3:28:49* @author <a href="mailto:447441478@qq.com">宋进宇</a>* 这种死锁是 a线程需要资源s1,s2 b线程同样需要s1,s2 但是获取的顺序不同* 死锁情况:a拿到了 s1 进行业务处理 ,此时, b拿到了 s2 也进行业务处理,* a 处理完有关s1的业务后需要拿到s2 继续处理 ,可是 此时 b处理完相关于* s2的相关业务后 需要拿到 s1 继续处理 这是就出现,a拿着s1 在等 s2 * b 拿着s2 在等s1 于是就出现了死锁*/
public class Demo {public static void main(String[] args) {Source1 s1 = new Source1();Source2 s2 = new Source2();Thread a = new Thread(new ThreadAA( s1, s2 ));Thread b = new Thread(new ThreadBB( s1, s2 ));a.start();b.start();}
}
class Source1{
}
class Source2{
}
class ThreadAA implements Runnable{private Source1 s1;private Source2 s2;public ThreadAA( Source1 s1, Source2 s2 ) {super();this.s1 = s1;this.s2 = s2;}@Overridepublic void run() {System.out.println( "ThreadAA 进来了" );synchronized ( s1 ) {System.out.println( "ThreadAA 拿到s1资源,还需要s2资源" );try {Thread.sleep( 2 );} catch (InterruptedException e) {}synchronized ( s2 ) {System.out.println( "ThreadAA 都拿到了" );}}}
}
class ThreadBB implements Runnable{private Source1 s1;private Source2 s2;public ThreadBB( Source1 s1, Source2 s2 ) {super();this.s1 = s1;this.s2 = s2;}@Overridepublic void run() {System.out.println( "ThreadBB 进来了" );synchronized ( s2 ) {System.out.println( "ThreadBB 拿到s2资源,还需要s1资源" );try {Thread.sleep( 2 );} catch (InterruptedException e) {}synchronized ( s1 ) {System.out.println( "ThreadBB 都拿到了" );}}}
}
死锁的解决
要从设计方面去解决避免,即在设计时就考虑不能出现死锁。
罗列出所有临界资源,画分布图,从图中观察其中的死锁情况,改变其中线程的(临界)资源的获取方式。
设计原则:尽量让程序中少出现临界资源。
同步设计的基本原则
◎ 同步块中(synchronized修饰)的代码越小越好!
◎ 同步块中不要写阻塞性代码(如,InputStream.read() )!
◎ 在持有锁的时候,不要对其它对象调用方法。(如果做到,可以消除最常见的死锁源头。)
同步概述
◎同步的原理:将需要同步的代码进行封装,并在该代码上加了一个锁。
◎同步的好处:解决多线程的安全问题。
◎同步的弊端:会降低性能。
◎同步的前提:必须要保证有多个线程且它们在同步中使用的是同一个锁。