生产消费者模型:

    1. public class Producer extends Thread{//生产者
    2. //为了保证生产者和消费者使用的是同一个仓库 添加一个属性
    3. private Warehouse warehouse;
    4. //传参数进来就可以保证 生产者和消费者使用的是同一个仓库
    5. public Producer(Warehouse warehouse){
    6. this.warehouse = warehouse;
    7. }
    8. //生产者的run方法 就是不断往仓库里放东西
    9. public void run(){
    10. while(true){
    11. warehouse.add();
    12. System.out.println("生产者往仓库存入了一件货物");
    13. try{
    14. Thread.sleep(200);
    15. }catch (Exception e){
    16. e.printStackTrace();
    17. }
    18. }
    19. }
    20. }
    21. public class Consumer extends Thread{//消费者
    22. private Warehouse warehouse;
    23. //传参数进来就可以保证 生产者和消费者使用的是同一个仓库
    24. public Consumer(Warehouse warehouse){
    25. this.warehouse = warehouse;
    26. }
    27. //消费者的run方法 一直从仓库拿东西
    28. public void run(){
    29. while(true){
    30. warehouse.get();
    31. System.out.println("消费者从仓库拿了一件货物");
    32. try {
    33. Thread.sleep(300);
    34. } catch (Exception e) {
    35. e.printStackTrace();
    36. }
    37. }
    38. }
    39. }
    40. public class Warehouse {//仓库
    41. //仓库里的集合 存放元素
    42. private ArrayList<String> list = new ArrayList<>();
    43. //这里ArrayList集合可能会引发线程非安全
    44. //假设仓库里有一件货物 却有两个消费者 消费者c1线程先执行了判断 紧接着消费者c2线程先执行了判断 此时集合里有一件货物
    45. //然后可能c2线程直接把货物拿走了 但是c1线程本来判断到仓库里有东西 但却被拿走了 这就会导致集合越界
    46. //往集合里添加元素
    47. public synchronized void add(){
    48. if(list.size()<20){
    49. list.add("a");
    50. }else{
    51. try {
    52. this.notifyAll();//在等待前先把其他在等待状态的线程全部唤醒
    53. this.wait();//仓库调用wait 但不是仓库对象等待 而是访问当前仓库的生产者线程进入等待状态
    54. } catch (Exception e) {
    55. e.printStackTrace();
    56. }
    57. }
    58. }
    59. //从集合里拿元素
    60. public synchronized void get(){//synchronized修饰 消费者调用时 就不会出现抢资源的问题
    61. if(list.size()>0) {
    62. list.remove(0);//集合越界问题
    63. }else {
    64. try {
    65. this.notifyAll();
    66. this.wait();//仓库调用wait 但不是仓库对象等待 而是访问当前仓库的消费者线程进入等待状态
    67. } catch (Exception e) {
    68. e.printStackTrace();
    69. }
    70. }
    71. }
    72. }
    73. public static void main(String[] args){
    74. Warehouse warehouse = new Warehouse();
    75. Producer p = new Producer(warehouse);
    76. //设置线程的优先级别 1-10 数越大级别越高
    77. //级别越高就更容易获取到CPU分配的时间碎片 但是不一定
    78. p.setPriority(10);//即唤醒后可能先让生产者干活
    79. Consumer c1 = new Consumer(warehouse);
    80. Consumer c2 = new Consumer(warehouse);
    81. p.start();
    82. c1.start();
    83. c2.start();
    84. }
    1. 当两个消费者同时访问同一个仓库对象时 可能会产生抢夺资源的问题
      1.1 集合越界
      1.2 异常IllegalMonitorStateException
      线程安全问题.png
      2. 解决线程安全问题
      让仓库对象被线程访问时 仓库对象被锁定
      仓库对象只能被一个线程访问 其他线程处于等待状态
      特征修饰符 synchronized 同步的 一个时间点只有一个线程访问
      也叫线程安全锁 锁定的是对象
      2.1 将synchronized关键字放在方法结构上
      public synchronized void get(){ }
      锁定的是调用方法时的那个对象
      比如 仓库里有get方法 当仓库对象在调用get方法时 仓库对象就被锁定了
      那么谁让仓库调用get方法呢?是消费者线程
      所以当仓库对象去get元素时 访问仓库的那个消费者线程就把这个仓库给锁上了
      2.2 可以将synchronized关键字放在方法(构造方法内 块内)的内部
      public void get(){
      好多代码
      synchronized(对象){

    好多代码
    }
    好多代码
    }
    3. 我们觉得return不是很好 return相当于让方法结束了
    应该让线程在不同状态来回切换
    wait() Object类中的方法
    对象.wait() 不是当前对象wait 而是访问当前这个对象的线程进入等待状态 即谁访问仓库谁等
    会产生一个类似假死状态 所有的线程都进入等待状态 此时没有线程做事
    notify() Object类中的方法
    如果只有两个线程 那么唤醒的就是另外一个线程 但是多线程时 随机唤醒
    notifyAll() Object类中的方法
    将线程全部唤醒
    4. 通过上述的生产消费者模型 可以做出一个完整且安全的模型
    4.1 利用线程安全锁 特征修饰符synchronized
    两种不同的写法 不管怎么写 锁定的都是对象
    4.2 利用方法控制线程状态来回操作
    wait() notify() notifyAll()
    4.3 Thread类中的方法
    sleep() 静态方法(参数long 毫秒值)
    setPriority(); getPriority();
    //设置 获取 线程的优先级1-10 数字越大优先级越高 更容易获取到CPU分配的时间碎片 但是不一定