这一节我们来说一个示例就是卖票示例:
需求:
我们现在有100张票,然后分四个窗口来卖,直到卖完为止。
思路:
1、先定一个一个票类,描述票的属性,还有打印卖出的票,并且实现Runnable中的run方法。
2、定义一个主方法,把这个类当成一个独立的运行程序。
3、在主方法当中创建4个线程来卖票。
代码:
1 class Ticket implements Runnable 2 { 3 4 int num = 100; 5 6 public void sellT() 7 { 8 9 while(true) 10 { 11 12 if(num>0) 13 System.out.println(num--); 14 15 } 16 17 } 18 19 public void run() 20 { 21 22 sellT(); 23 24 } 25 26 } 27 28 class ThreadTickets 29 { 30 31 public static void main(String[] args) { 32 33 Ticket p = new Ticket(); 34 35 Thread t1 = new Thread(p); 36 Thread t2 = new Thread(p); 37 Thread t3 = new Thread(p); 38 39 t1.start(); 40 t2.start(); 41 t3.start(); 42 43 } 44 45 }
我们写完之后,在不是很慢的电脑上运行都是正常的,但是我们的这个程序总体上安全么?如果在if(num>0)这个地方线程一刚要执行的时候被切换出去了那么num此时如果恰巧是1的话,那么此时线程二进来后1>0之后就直接打印1,但是在线程一苏醒之后,这时候他不再判断num是否大于零而是直接打印了 num--这个时候就出现了0甚至是负数。这就是多线程当中出现的问题,那么如何解决这个问题。我们可以试想一下,如果在线程一退出的时候,能够让num不让其他对象访问,直到线程一苏醒之后处理完num之后,再让出num的控制权的话,这样是不是很安全呢,这个也叫做多线程当中的同步。同步是指,在同时有多个线程运行的时候,其中有一段代码块一个时候只能允许一个线程来处理,只有这个线程处理完了之后,其他的线程才有机会重新掌管这个代码块,周而复始。
java为了能够让我们处理上述的不同步问题就是一个线程执行的同时,其中共享的资源分别被其他的线程来执行着,这样就可能造成输出了不该输出的语句。为我们提供了一个关键字叫做synchronized。这个关键词的使用方法,是指定一个锁,来访问其内部的代码块。如果一个线程要处理访问其中的代码块的话,必须先拿到锁。并且这个锁要是所有的线程公用的锁才好,这样才能够起到锁的作用。当一个线程拿到锁之后,其他线程来访问的话如果没有锁,则一直判断另一个线程的锁是否释放,只有持有锁的线程把任务执行完之后,才会释放锁。这个锁一释放之后,其他的线程就有机会持有这个锁,然后进行任务的操作。这样就在多线程的情况下,保证了在某一个时刻,只能由一个线程来执行这段同步代码块。也保证了共享数据的安全性,不至于这段数据在被修改之后,其他线程仍然认为这个数据就是之前的按个数据的问题。究竟如何使用呢,代码如下:
1 class Ticket implements Runnable 2 { 3 4 int num = 100; 5 6 Object obj = new Object(); 7 8 public void sellT() 9 { 10 11 while(true) 12 { 13 synchronized (obj) 14 { 15 16 if(num>0) 17 { 18 try 19 { 20 21 Thread.sleep(10); 22 23 }catch(InterruptedException e) 24 { 25 26 27 } 28 System.out.println("The CurrentThread is:"+Thread.currentThread().getName()+"The num is:"+num--); 29 30 } 31 32 } 33 } 34 35 } 36 37 public void run() 38 { 39 40 sellT(); 41 42 } 43 44 } 45 46 class ThreadTickets 47 { 48 49 public static void main(String[] args) { 50 51 Ticket p = new Ticket(); 52 53 Thread t1 = new Thread(p); 54 Thread t2 = new Thread(p); 55 Thread t3 = new Thread(p); 56 57 t1.start(); 58 t2.start(); 59 t3.start(); 60 61 } 62 63 }
其中同步代码块还有一种简写的形式,也就是说把代码块关键字synchronized( lock ){},把synchronized直接放到函数上,这样整个函数就是同步的了。也就是:
1 class Ticket implements Runnable 2 { 3 4 int num = 100; 5 6 Object obj = new Object(); 7 8 public void sellT() 9 { 10 11 while(true) 12 { 13 try 14 { 15 16 Thread.sleep(100); 17 18 }catch(InterruptedException e) 19 { 20 21 22 } 23 pTicket(); 24 25 } 26 27 } 28 29 public synchronized void pTicket() 30 { 31 32 if(num>0) 33 { 34 35 System.out.println("The CurrentThread is:"+Thread.currentThread().getName()+"The num is:"+num--); 36 37 } 38 39 } 40 41 public void run() 42 { 43 44 sellT(); 45 46 } 47 48 } 49 50 class ThreadTickets 51 { 52 53 public static void main(String[] args) { 54 55 Ticket p = new Ticket(); 56 57 Thread t1 = new Thread(p); 58 Thread t2 = new Thread(p); 59 Thread t3 = new Thread(p); 60 61 t1.start(); 62 t2.start(); 63 t3.start(); 64 65 } 66 67 }
synchronized这个关键字加到代码块之前就是同步代码块,加到函数上面就是同步函数。