多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 同步后,就不会有多个线程同时执行这段代码了。

需求

动物园售票,一共100张票,通过四个窗口同时售卖,卖完停止。

使用继承Thread实现

  1. /**
  2. * 售票窗口
  3. */
  4. class TicketWindow extends Thread {
  5. /**
  6. * 总票数
  7. */
  8. private static int ticket = 100;
  9. /**
  10. * 窗口名称
  11. */
  12. private String windowName;
  13. //private static Object lock = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的,否则,每次创建当前对象都是一个新的对象
  14. public TicketWindow(String windowName) {
  15. super(windowName);
  16. this.windowName = windowName;
  17. }
  18. @Override
  19. public void run() {
  20. //开票卖票
  21. doSale();
  22. }
  23. public void doSale(){
  24. while(true) {
  25. synchronized(TicketWindow.class) { //如果使用this,不会起到作用,因为是4个TicketWindow对象卖票,不是同一个对象
  26. if(ticket <= 0) {
  27. //停止卖票
  28. break;
  29. }
  30. try {
  31. Thread.sleep(10);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println(getName() + "卖掉第 " + ticket-- + " 号票");
  36. }
  37. }
  38. }
  39. }
  1. public static void main(String[] args) {
  2. /**
  3. * 4个窗口卖票
  4. */
  5. new TicketWindow("1号窗口").start();
  6. new TicketWindow("2号窗口").start();
  7. new TicketWindow("3号窗口").start();
  8. new TicketWindow("4号窗口").start();
  9. }
  1. ......
  2. ......
  3. ......
  4. 1号窗口卖掉第 4 号票
  5. 4号窗口卖掉第 4 号票
  6. 2号窗口卖掉第 4 号票
  7. 2号窗口卖掉第 3 号票
  8. 4号窗口卖掉第 3 号票
  9. 3号窗口卖掉第 3 号票
  10. 1号窗口卖掉第 3 号票
  11. 2号窗口卖掉第 2 号票
  12. 4号窗口卖掉第 0 号票
  13. 1号窗口卖掉第 1 号票
  14. 3号窗口卖掉第 2 号票

观察上面输出结果,发现同一张票会被多个窗口多次卖出。

  1. 1号窗口卖掉第 100 号票
  2. 1号窗口卖掉第 99 号票
  3. 4号窗口卖掉第 98 号票
  4. 4号窗口卖掉第 97 号票
  5. 3号窗口卖掉第 96 号票
  6. 3号窗口卖掉第 95 号票
  7. 3号窗口卖掉第 94 号票
  8. 3号窗口卖掉第 93 号票
  9. 3号窗口卖掉第 92 号票
  10. 3号窗口卖掉第 91 号票
  11. 3号窗口卖掉第 90 号票
  12. 3号窗口卖掉第 89 号票
  13. 3号窗口卖掉第 88 号票
  14. 3号窗口卖掉第 87 号票
  15. 3号窗口卖掉第 86 号票
  16. 3号窗口卖掉第 85 号票
  17. 3号窗口卖掉第 84 号票
  18. 2号窗口卖掉第 83 号票
  19. 2号窗口卖掉第 82 号票
  20. 2号窗口卖掉第 81 号票
  21. 3号窗口卖掉第 80 号票
  22. 4号窗口卖掉第 79 号票
  23. 4号窗口卖掉第 78 号票
  24. 1号窗口卖掉第 77 号票
  25. 1号窗口卖掉第 76 号票
  26. 1号窗口卖掉第 75 号票
  27. 1号窗口卖掉第 74 号票
  28. 1号窗口卖掉第 73 号票
  29. 1号窗口卖掉第 72 号票
  30. 4号窗口卖掉第 71 号票
  31. 4号窗口卖掉第 70 号票
  32. 4号窗口卖掉第 69 号票
  33. 4号窗口卖掉第 68 号票
  34. 4号窗口卖掉第 67 号票
  35. 3号窗口卖掉第 66 号票
  36. 2号窗口卖掉第 65 号票
  37. 3号窗口卖掉第 64 号票
  38. 3号窗口卖掉第 63 号票
  39. 3号窗口卖掉第 62 号票
  40. 4号窗口卖掉第 61 号票
  41. 1号窗口卖掉第 60 号票
  42. 1号窗口卖掉第 59 号票
  43. 1号窗口卖掉第 58 号票
  44. 1号窗口卖掉第 57 号票
  45. 4号窗口卖掉第 56 号票
  46. 4号窗口卖掉第 55 号票
  47. 4号窗口卖掉第 54 号票
  48. 4号窗口卖掉第 53 号票
  49. 4号窗口卖掉第 52 号票
  50. 3号窗口卖掉第 51 号票
  51. 2号窗口卖掉第 50 号票
  52. 2号窗口卖掉第 49 号票
  53. 2号窗口卖掉第 48 号票
  54. 3号窗口卖掉第 47 号票
  55. 4号窗口卖掉第 46 号票
  56. 1号窗口卖掉第 45 号票
  57. 1号窗口卖掉第 44 号票
  58. 1号窗口卖掉第 43 号票
  59. 1号窗口卖掉第 42 号票
  60. 1号窗口卖掉第 41 号票
  61. 1号窗口卖掉第 40 号票
  62. 1号窗口卖掉第 39 号票
  63. 1号窗口卖掉第 38 号票
  64. 1号窗口卖掉第 37 号票
  65. 1号窗口卖掉第 36 号票
  66. 1号窗口卖掉第 35 号票
  67. 1号窗口卖掉第 34 号票
  68. 1号窗口卖掉第 33 号票
  69. 1号窗口卖掉第 32 号票
  70. 1号窗口卖掉第 31 号票
  71. 1号窗口卖掉第 30 号票
  72. 1号窗口卖掉第 29 号票
  73. 1号窗口卖掉第 28 号票
  74. 1号窗口卖掉第 27 号票
  75. 1号窗口卖掉第 26 号票
  76. 1号窗口卖掉第 25 号票
  77. 1号窗口卖掉第 24 号票
  78. 1号窗口卖掉第 23 号票
  79. 1号窗口卖掉第 22 号票
  80. 1号窗口卖掉第 21 号票
  81. 1号窗口卖掉第 20 号票
  82. 1号窗口卖掉第 19 号票
  83. 4号窗口卖掉第 18 号票
  84. 3号窗口卖掉第 17 号票
  85. 2号窗口卖掉第 16 号票
  86. 2号窗口卖掉第 15 号票
  87. 2号窗口卖掉第 14 号票
  88. 2号窗口卖掉第 13 号票
  89. 2号窗口卖掉第 12 号票
  90. 3号窗口卖掉第 11 号票
  91. 3号窗口卖掉第 10 号票
  92. 3号窗口卖掉第 9 号票
  93. 3号窗口卖掉第 8 号票
  94. 3号窗口卖掉第 7 号票
  95. 3号窗口卖掉第 6 号票
  96. 4号窗口卖掉第 5 号票
  97. 1号窗口卖掉第 4 号票
  98. 1号窗口卖掉第 3 号票
  99. 4号窗口卖掉第 2 号票
  100. 3号窗口卖掉第 1 号票

使用Runnable接口实现

  1. /**
  2. * 需求:
  3. * 动物园售票,一共100张票,通过四个窗口售卖,卖完停止。
  4. * 使用Runnable接口实现
  5. * @author Axin
  6. */
  7. public class Demo04SaleTickets {
  8. public static void main(String[] args) {
  9. //所有的票
  10. Ticket tickets = new Ticket();
  11. //4个窗口卖票
  12. new Thread(tickets,"1号窗口").start();
  13. new Thread(tickets,"2号窗口").start();
  14. new Thread(tickets,"3号窗口").start();
  15. new Thread(tickets,"4号窗口").start();
  16. /**
  17. * 多次启动线程同一个线程是不合法的。 特别是,一旦线程完成执行不能再重新启动。
  18. * 异常
  19. * IllegalThreadStateException - 如果线程已经启动。
  20. * ---来自JDK文档
  21. *
  22. */
  23. //Thread t1 = new Thread(tickets);
  24. //t1.start();
  25. //t1.start();
  26. //t1.start();
  27. //t1.start();
  28. }
  29. }
  30. /**
  31. * 所有的票
  32. */
  33. class Ticket implements Runnable {
  34. /**
  35. * 总票数
  36. */
  37. private int ticket = 100;
  38. @Override
  39. public void run() {
  40. //开票卖票
  41. doSale();
  42. }
  43. public void doSale(){
  44. while(true) {
  45. synchronized(this) {
  46. if(ticket <= 0) {
  47. //停止卖票
  48. break;
  49. }
  50. try {
  51. Thread.sleep(100);
  52. } catch (InterruptedException e) {
  53. e.printStackTrace();
  54. }
  55. System.out.println(Thread.currentThread().getName() + "卖掉第 " + ticket-- + " 号票");
  56. }
  57. }
  58. }
  59. }