多线程(2)- 线程同步

亦凉 2021-05-03 16:04 757阅读 0赞

卖票的例子

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. public void run()
  4. {
  5. while(true)
  6. {
  7. if(tick>0)
  8. {
  9. System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
  10. }
  11. }
  12. }
  13. }
  14. class TicketDemo {
  15. public static void main(String[] args)
  16. {
  17. Ticket t = new Ticket();
  18. Thread t1 = new Thread(t);//创建了4个线程;
  19. Thread t2 = new Thread(t);
  20. Thread t3 = new Thread(t);
  21. Thread t4 = new Thread(t);
  22. t1.start();
  23. t2.start();
  24. t3.start();
  25. t4.start();
  26. }
  27. }

事实上,该代码是存在安全问题的。当余票为1时,某线程符合if(tick>0),进入if里的执行语句,但是刚好CPU切到别的地方执行别的程序,当前线程具有执行资格但没有执行权,需要等待。而此时tick仍然等于1,别的线程仍能进入if里的执行语句,同样进行等待。当CPU回来处理该块代码时,就会发生多个线程都要被执行,打印出0,-1等错票
出现该问题的原因是:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
Java对于多线程的安全问题提供了专业的解决方式-同步代码块。

同步代码块

synchronized(对象)
{
需要被同步的代码
}
对象如同锁,这里可以是任意对象。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

改进后代码:

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. Object obj=new Object();
  4. public void run()
  5. {
  6. while(true)
  7. {
  8. synchronized(obj)
  9. {
  10. if(tick>0)
  11. {
  12. System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
  13. }
  14. }
  15. }
  16. }
  17. }
  18. class TicketDemo {
  19. public static void main(String[] args)
  20. {
  21. Ticket t = new Ticket();
  22. Thread t1 = new Thread(t);//创建了4个线程;
  23. Thread t2 = new Thread(t);
  24. Thread t3 = new Thread(t);
  25. Thread t4 = new Thread(t);
  26. t1.start();
  27. t2.start();
  28. t3.start();
  29. t4.start();
  30. }
  31. }

同步的前提:

  1. 必须要有两个或者两个以上的线程。
  2. 必须是多个线程使用同一个锁。
  3. 必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。
弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形
中会降低程序的运行效率。

同步函数

上面的Ticket也可以写成同步函数的形式

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. public void run()
  4. {
  5. while(true)
  6. {
  7. show();
  8. }
  9. }
  10. public synchronized void show()
  11. {
  12. if(tick>0)
  13. {
  14. System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
  15. }
  16. }
  17. }
  • 需要注意的是需要同步的代码是哪一段:如果直接将run函数用synchronized修饰显然是不对的,需要同步的是里面的代码,所以这个地方新建一个函数,并用synchronized修饰,并在run方法里进行调用。
  • 在上面的同步代码块中,锁对应的对象是我们创建的Object类的对象。而在同步函数中,锁对应的对象是 this。
  • 在静态同步函数中,锁对应的对象为该方法所在类的字节码文件对象,即 类名.class

死锁

两个线程相互等待对方释放资源锁(即刚好A线程要用到B线程锁定的一个资源,而B线程中刚好也要用到A线程中锁定的一个资源),两个线程相互等待。
任何操作系统都无法完全避免死锁,所以编程时一定要注意,特别是在同步锁特别多的情况下死锁多发。
eg:

  1. class Demo implements Runnable {
  2. private boolean flag;
  3. Demo(boolean flag)
  4. {
  5. this.flag = flag;
  6. }
  7. public void run()
  8. {
  9. if(flag)
  10. {
  11. while(true)
  12. {
  13. synchronized(MyLock.locka)
  14. {
  15. System.out.println(Thread.currentThread().getName()+"...if locka ");
  16. synchronized(MyLock.lockb)
  17. {
  18. System.out.println(Thread.currentThread().getName()+"..if lockb");
  19. }
  20. }
  21. }
  22. }
  23. else
  24. {
  25. while(true)
  26. {
  27. synchronized(MyLock.lockb)
  28. { System.out.println(Thread.currentThread().getName()+"...else lockb");
  29. synchronized(MyLock.locka)
  30. {
  31. System.out.println(Thread.currentThread().getName()+".....else locka");
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. class MyLock {
  39. static Object locka = new Object();
  40. static Object lockb = new Object();
  41. }
  42. class Test {
  43. public static void main(String[] args)
  44. {
  45. Thread t1 = new Thread(new Demo(true));
  46. Thread t2 = new Thread(new Demo(false));
  47. t1.start();
  48. t2.start();
  49. }
  50. }

在这个例子中,flag为true时拥有locka锁的线程没有lockb锁,flag为false时拥有lockb锁的线程没有locka锁,都无法进入下一个输出语句代码块,僵持不下产生死锁

发表评论

表情:
评论列表 (有 0 条评论,757人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java线-线同步简述

    为什么要线程同步 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问机制上的冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(sy

    相关 线-线同步

    线程同步:协调多个线程间的并发操作,以获得符合预期的,确定的执行结果,消除多线程应用程序的不确定性. 使用线程的同步:可以保护资源同时只能由一个线程访问,一般采取的措施是获取

    相关 线同步

    第一种同步方式:ReentrantLock类 一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,它们被阻塞,直到第一个线程释放锁对象。如

    相关 Java线线同步

    线程安全问题 在单线程中不会出现线程安全问题,而多线程编程中,如果多个线程同时操作同一个资源,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等