1、临界资源问题

—————临界资源:多个线程共享的数据

  1. public class Stack {
  2. int idx=0;
  3. char[] data=new char[10];
  4. public void push(char c) {
  5. synchronized (this) { 在执行该代码段时必须取得对象锁
  6. data[idx]=c;
  7. idx++;
  8. }
  9. }
  10. public synchronized char pop() { 在执行该方法时必须取得对象锁
  11. idx--;
  12. return data[idx];
  13. }
  14. }

如何解决多线程安全问题

  • 基本思想:让程序没有安全问题的环境

    怎么实现

  • 把多条语句操作共享的代码锁起来,任意时刻只执行一个线程

  • java提供同步代码块

    同步代码块

    synchronized 的作用

  • 给调用方法的对象加锁,保证一个方法处理的对象资源不会因其他方法的执行而改变。

  • 使用方法:
    1. 用在对象前面限制一段代码的执行;
    2. 方法前,表示该方法为同步方法。
  • 格式:

synchronized (任意对象){
多条语句操作共享数据的代码
}
image.png
执行同步代码的过程

  1. 问题描述:
  2. SellTick类:
  3. 1、定义类实现Runnable接口
  4. 2、覆盖Runnable接口的run方法。将线程要运行的代码存放在该run方法中。
  5. SellTicketDemo
  6. 3、通过Thread类建立线程对象。
  7. 4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
  8. 为什么要将Runnable接口的子类对象传递给Thread的构造函数。
  9. 因为,自定义的run方法所属的对象的是Runnable 的接口的子类对象。所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。
  10. 5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
  11. //class SellTick implements Runnable{ 由于线程执行的随机性会出现票的序号重复和出现负数现象
  12. // private static int tickets=100;
  13. // public void run(){
  14. // while(true){
  15. // if(tickets>0)
  16. // {
  17. // System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
  18. // tickets--;
  19. // try {
  20. // Thread.sleep(100);
  21. // } catch (InterruptedException e) {
  22. // throw new RuntimeException(e);
  23. // }
  24. // }
  25. // }
  26. //
  27. // }
  28. //}
  29. class SellTick implements Runnable{
  30. private static int tickets=100;
  31. private Object obj=new Object();
  32. public void run(){
  33. synchronized (obj){ //三个线程使用同一把锁,不能使用synchronized (new Objct()),
  34. while(true){
  35. if(tickets>0)
  36. {
  37. try {
  38. Thread.sleep(100);
  39. } catch (InterruptedException e) {
  40. throw new RuntimeException(e);
  41. }
  42. System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
  43. tickets--;
  44. }
  45. }
  46. }
  47. }
  48. }
  49. public class SellTicketDemo {
  50. public static void main(String[] args) {
  51. SellTick se=new SellTick();
  52. Thread t1=new Thread(se,"窗口1");
  53. Thread t2=new Thread(se,"窗口2");
  54. Thread t3=new Thread(se,"窗口3");
  55. t1.start();
  56. t2.start();
  57. t3.start();
  58. }
  59. }

2 wait()和notify() 方法

wait( )释放对象锁,进入等待阻塞状态
notify( )通知等待者执行(随机选择)

  • 这两个方法配套使用
  • 使用要求:
    • 必须在 synchronized 方法或块中调用。因为只有在同步代码段中才存在资源锁定。
    • 这对方法直接隶属于Object 类,而不是Thread类。
  • 采用wait和notify可以解决很多临界访问控制问题 ```java class Bridge { private boolean engaged = false; //桥的占用状态

    public synchronized void getBridge( ) { //取得上桥资格

    1. while (engaged ) {
    2. try {
    3. wait( ); //如果桥被占用就循环等待
    4. } catch ( InterruptedException exception ) { }
    5. }
    6. engaged = true; //占用桥

    }

    public synchronized void goDownBridge( ) {//下桥

    1. engaged = false;
    2. notifyAll( ); //唤醒其他等待线程

    } }

class PersonPassBridge extends Thread { private Bridge bridge; //桥对象 String id; //人的标识 public PersonPassBridge (String id, Bridge b ) { bridge = b; this.id=id; } public void run( ) { bridge.getBridge(); //等待过桥 System.out.println(id +”正过桥…”); try { Thread.sleep((int)(Math.random()* 1000)); } catch( InterruptedException exception ) { } bridge.goDownBridge( ); //下桥 } }

public class Test{ public static void main( String args[ ] ) { Bridge b =new Bridge( ); PersonPassBridge x; for (int k=1;k<=4;k++) { x = new PersonPassBridge(“南边,第”+k+”人”, b); x.start( ); } for (int k=1;k<=3;k++) { x = new PersonPassBridge(“北边,第”+k+”人”, b); x.start( ); } } } 南边,第1人正过桥… 南边,第2人正过桥… 北边,第3人正过桥… 南边,第3人正过桥… 北边,第2人正过桥… 南边,第4人正过桥… 北边,第1人正过桥… ```