此文章是转载的,因为自己看了,感觉特别好。
用一个卖票的例子来示范 多线程 操作一个对象时,同步锁的使用。
先新建一个SaleRunnable类实现Runnable接口,用于表示“卖票”过程:
class SaleRunnable implements Runnable {
如上,一共有20张票,每卖出一张票,则打印卖出此票的线程名和票的序号。这个代码里没有加锁。
然后在主程序创建三个 线程 ,传入同一个SaleRunnable对象,三个线程名分别为老王,老张,老李:
SaleRunnable r = new SaleRunnable(); Thread t1 = new Thread(r,"老王"); Thread t2 = new Thread(r,"老张"); Thread t3 = new Thread(r,"老李");12341234
然后调用线程的 start ()方法启动三个线程:
t1.start();
运行结果如下:
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第1张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第2张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第3张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第4张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第5张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第6张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第7张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第8张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第9张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第10张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第11张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第12张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第13张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第14张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第15张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第16张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第17张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第18张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第19张票08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第20张票08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第12张票123456789101112131415161718192021123456789101112131415161718192021
可见其结果是乱的,重复运行多次,结果不一,但是都没实现正常的卖票效果。为什么呢,因为三个线程在并行操作同一个对象,所以会出现老张的线程和老王的线程都卖出了第12张票的情况,但是这显然是不对的。
怎么办呢,修改下SaleRunnable类,加上锁试试:
class SaleRunnable implements Runnable {
如上面所示,把锁加在判断条件里面,运行结果:
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第1张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第2张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第3张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第4张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第5张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第6张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第7张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第8张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第9张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第10张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第11张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第12张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第13张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第14张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第15张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第16张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第17张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第18张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第19张票08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第20张票08-26 13:15:08.631 15256-15273/? D/Test: 老李卖出了第21张票08-26 13:15:08.631 15256-15272/? D/Test: 老张卖出了第22张票12345678910111213141516171819202122231234567891011121314151617181920212223
结果看,虽然卖票顺序对了,但卖多了也是不正常的,再改下:
class SaleRunnable implements Runnable {
运行结果:
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第1张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第2张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第3张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第4张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第5张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第6张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第7张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第8张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第9张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第10张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第11张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第12张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第13张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第14张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第15张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第16张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第17张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第18张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第19张票08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第20张票12345678910111213141516171819201234567891011121314151617181920
结果是没错,但是票全让老王给卖了,这也不正常,因为我们把锁加在整个卖票操作外面了,一旦老王开始卖票,其他人都没法卖了。再改:
class SaleRunnable implements Runnable {private int ticket = 20;@Override public void run() { while(true) {//判断条件做了修改synchronized(this) {//锁加在循环判断里面,只有满足条件才能执行卖票操作if (ticket > 0) {Log.d("Test", Thread .currentThread().getName() + "卖出了第" + (20 - ticket + 1) + "张票");ticket--;} else { break;}} try {//这里的操作是为了方便线程间自动切换,如果不加的话,可能结果也是票全让老王线程给卖了Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}}1234567891011121314151617181920212223242512345678910111213141516171819202122232425
这样修改后结果正常了:
08-26 13:43:24.181 12176-12199/? D/Test: 老王卖出了第1张票08-26 13:43:24.181 12176-12200/? D/Test: 老张卖出了第2张票08-26 13:43:24.181 12176-12201/? D/Test: 老李卖出了第3张票08-26 13:43:27.181 12176-12199/?D/Test: 老王卖出了第4张票08-26 13:43:27.181 12176-12200/?D/Test: 老张卖出了第5张票08-26 13:43:27.181 12176-12201/?D/Test: 老李卖出了第6张票08-26 13:43:30.181 12176-12199/?D/Test: 老王卖出了第7张票08-26 13:43:30.181 12176-12200/?D/Test: 老张卖出了第8张票08-26 13:43:30.181 12176-12201/?D/Test: 老李卖出了第9张票08-26 13:43:33.181 12176-12199/?D/Test: 老王卖出了第10张票08-26 13:43:33.181 12176-12200/?D/Test: 老张卖出了第11张票08-26 13:43:33.181 12176-12201/?D/Test: 老李卖出了第12张票08-26 13:43:36.181 12176-12199/?D/Test: 老王卖出了第13张票08-26 13:43:36.181 12176-12200/?D/Test: 老张卖出了第14张票08-26 13:43:36.191 12176-12201/?D/Test: 老李卖出了第15张票08-26 13:43:39.181 12176-12199/?D/Test: 老王卖出了第16张票08-26 13:43:39.181 12176-12200/?D/Test: 老张卖出了第17张票08-26 13:43:39.191 12176-12201/?D/Test: 老李卖出了第18张票08-26 13:43:42.181 12176-12199/?D/Test: 老王卖出了第19张票08-26 13:43:42.181 12176-12200/?D/Test: 老张卖出了第20张票12345678910111213141516171819201234567891011121314151617181920
因此多个线程对同一个线程进行操作时,加锁是有效的解决冲突的方式,但是加到哪里一定要多多思考。