进阶Java编程(3)线程的同步与死锁
线程的同步与死锁
1,同步问题引出
在多线程的处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,对于当多个线程访问统一资源的时候如果处理不当就会产生数据的错误操作。
①同步问题的引出
下面编写一个买票程序,将创建若干个线程的对象实现卖票处理操作。
·范例:实现卖票操作
1 class MyThread implements Runnable{ 2 private int ticket=10;//总票数为10张 3 @Override 4 public void run() { 5 while (true){ 6 if(this.ticket>0){ 7 try{ 8 Thread.sleep(100); 9 }catch(InterruptedException e){ 10 e.printStackTrace(); 11 } 12 System.out.println(Thread.currentThread().getName()+"卖票,ticket="+this.ticket--); 13 }else { 14 System.out.println("****** 票已经卖光了 *********"); 15 break; 16 } 17 } 18 } 19 } 20 public class Main { 21 public static void main(String[] args) { 22 MyThread myThread=new MyThread(); 23 new Thread(myThread,"票贩子A").start(); 24 new Thread(myThread,"票贩子B").start(); 25 new Thread(myThread,"票贩子C").start(); 26 new Thread(myThread,"票贩子D").start(); 27 new Thread(myThread,"票贩子E").start(); 28 } 29 }
1 票贩子A卖票,ticket=0 2 ****** 票已经卖光了 ********* 3 票贩子B卖票,ticket=1 4 ****** 票已经卖光了 ********* 5 票贩子C卖票,ticket=-2 6 ****** 票已经卖光了 ********* 7 票贩子D卖票,ticket=-3 8 ****** 票已经卖光了 ********* 9 ****** 票已经卖光了 *********
此时的程序将创建三个线程对象,并且这三个线程对象将进行5章票的出售。此时的程序在进行卖票处理的时候并没有任何的问题(这是假象),下面可以模拟一下卖票过程中的延迟操作。
2,线程同步处理
经过分析之后已经可以确定同步问题所产生的主要原因了,那么下面就需要进行进行同步问题的关键是锁,指的是当某一个线程执行操作的时候,其他线程外面等待;
如果想要在程序之中实现这把锁的功能,就可以使用synchronized关键字来实现,利用此关键字可以定义同步方法或同步代码块,在同步代码块的操作里面的代码只允许一个线程执行。
①利用同步代码块进行处理:
1 synchronized (同步操作){ 2 同步代码操作; 3 }
·范例:利用同步代码块解决数据同步访问问题一般要进行同步对象处理的时候可以采用当前对象this进行同步。
1 class MyThread implements Runnable{ 2 private int ticket=10;//总票数为10张 3 @Override 4 public void run() { 5 while (true){ 6 synchronized(this){//每一次只允许一个线程进行执行 7 if(this.ticket>0){ 8 try{ 9 Thread.sleep(100); 10 }catch(InterruptedException e){ 11 e.printStackTrace(); 12 } 13 System.out.println(Thread.currentThread().getName()+"卖票,ticket="+this.ticket--); 14 }else { 15 System.out.println("* 票已经卖光了 *"); 16 break; 17 } 18 } 19 } 20 } 21 } 22 public class Main { 23 public static void main(String[] args) { 24 MyThread myThread=new MyThread(); 25 new Thread(myThread,"票贩子A").start(); 26 new Thread(myThread,"票贩子B").start(); 27 new Thread(myThread,"票贩子C").start(); 28 } 29 }
②利用同步方法解决:只需要在方法定义上使用synchronized关键字即可加入同步处理之后,程序的整体的执行的性能下降了。同步实际上会造成性能的降低。
1 class MyThread implements Runnable{ 2 private int ticket=10;//总票数为10张 3 public synchronized boolean sale(){ 4 if(this.ticket>0){ 5 try{ 6 Thread.sleep(100); 7 }catch(InterruptedException e){ 8 e.printStackTrace(); 9 } 10 System.out.println(Thread.currentThread().getName()+"卖票,ticket="+this.ticket--); 11 return true; 12 }else { 13 System.out.println("****** 票已经卖光了 *********"); 14 return false; 15 } 16 } 17 @Override 18 public void run() { 19 while (this.sale()){ 20 ;//空语句 21 } 22 } 23 } 24 public class Main { 25 public static void main(String[] args) { 26 MyThread myThread=new MyThread(); 27 new Thread(myThread,"票贩子A").start(); 28 new Thread(myThread,"票贩子B").start(); 29 new Thread(myThread,"票贩子C").start(); 30 } 31 }
在日后学习Java类库的时候会发现,系统中使用的类上使用的许多同步处理采用的都是同步方法。
注意:同步会造成性能的下降,并发才会高性能。
3,线程死锁
死锁是在进行多线程同步的处理之中有可能产生的一种问题,所谓的死锁指的是若干个线程彼此互相等待的状态。
·范例:通过一个简单的代码观察死锁
1 public class DeadLock implements Runnable{ 2 private Jian jj =new Jian(); 3 private XiaoQiang xq=new XiaoQiang(); 4 5 @Override 6 public void run() { 7 jj.say(xq); 8 } 9 public DeadLock(){//构造方法 10 new Thread(this).start(); 11 xq.say(jj); 12 } 13 public static void main(String[] args) { 14 new DeadLock(); 15 } 16 } 17 18 class Jian{ 19 public synchronized void say(XiaoQiang xq){ 20 System.out.println("阿建说:此路是我开留下买路财"); 21 xq.get(); 22 } 23 public synchronized void get(){ 24 System.out.println("阿建说:获得过路费买饭吃"); 25 } 26 } 27 class XiaoQiang{ 28 public synchronized void say(Jian jj){ 29 System.out.println("小强说:先过路在给钱"); 30 jj.get(); 31 } 32 public synchronized void get(){ 33 System.out.println("小强说:通过道路去上班"); 34 } 35 }
若干线程访问同一资源时一定要进行同步处理,而过多的同步会造成死锁。现在死锁造成的主要原因是因为彼此都在互相等待着,等待着对方先让出资源。死锁是开发中的一种不确定的状态,有的时候代码处理不当则会不定期出现死锁,这是属于正常开发中的调试问题。