• 多线程编程的资源争用问题


    一个 基本概念

         (1)server运行任务必须等待一个任务后运行,只要运行的下一个任务。所谓同步(Synchronized)。任务发布运行命令,此时server能够接受要运行的命令,那接到任务后,,server也能够干其它事称为异步。

     同步和异步的差别 
     举个样例:普通B/S模式(同步)AJAX技术(异步)
    同步:提交请求->等待server处理->处理完成返回 这个期间client浏览器不能干不论什么事
    异步: 请求通过事件触发->server处理(这是浏览器仍然能够作其它事情)->处理完成

         (2)实现实现多线程编程的两种方法:

         A.继承类Thread(所属的jar包 java.lang.Thread)。

    重写run方法。简单样例

         Publicclass ThreadA extends Thread{

         Publicvoid run(){//重写方法

           Sleep();//直接调用由于继承自父类 父类中含有这种方法

    }

         Publicvoid main(String []ars)

    {

           ThreadA thread=new Thread();

           Thread.start();//这里直接调用方法就可以

         }

    }

    B.实现接口 Runnable,重写run方法。简单样例:

    Public class ThreadB implements Runnable{

         Publicvoid run(){//重写方法

          Thread.sleep();// 假设须要调用sleep()方法的话,须要Thread静态方法。由于这里仅仅是实现了接口

    }

         Publicvoid main(String []ars)

    {

           ThreadB threadB=new ThreadB();

           (newThreadB(threadB)).start();//这里使用了装饰者模式注意两种方法启动线程的差别由于仅仅是实现了接口,并没有实现 start方法。

        }

    }

    (3) 线程概念 以及五个状态

    一般操作系统都支持同一时候执行多个任务,每一个任务就是一个程序。系统级别的任务称之为进程,一个程序又能够同一时候执行多个任务。程序级别的任务称之为线程

    线程与进程的详细差别:

    http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html

    进程是并发的,OS 将时间划分为若干个时间片。在每一个时间片段中运行不

    同的任务。

    OS 尽可能均匀地将时间片分给每一个任务,所以在微观上,全部的程序都是走走停停。在宏观上全部程序都在同一时候执行,这样的现象叫做并发。

      线程的五个状态:new,runnable,running, block ,dead.

    Thread.yeald() 方法会主动让出此CPU 时间,主动从running 状态回到runnable。但让出的时间不可控。


    java中,每一个线程都需经历新生(new)、就绪(runnable)、执行(running)、堵塞(block)和死亡(dead)五种状态,线程从新生到死亡的状态变化称为生命周期
    new运算符Thread类或其子类建立一个线程对象后。该线程就处于新生状态。
       新生(new)--->就绪(runnable):通过调用start()方法
       就绪(runnable)--->执行(running):处于就绪状态的线程一旦得到CPU,就进入执行状态并自己主动调用自己的run()方法
       执行--->堵塞:处于执行状态的线程,执行sleep()方法。或等待I/O设备资源。让出CPU并临时中止自己执行。进入堵塞状态
       堵塞--->就绪:睡眠时间已到,或等待的I/O设备空暇下来,线程便进入就绪状态,又一次到就绪队列中等待CPU

    当再次获得CPU时,便从原来中止位置開始继续执行。    执行--->死亡:(1)(正常情况下)线程任务完毕                (2)(非正常状况)线程被强制性的中止,如通过执行stop()destroy()方法来终止一个线程

     

    (4)wait sleep notify方法以及synchronized()进程块

     4.1 wait与sleep

    Sleep 是线程类(Thread)的方法(调用sleep方法由运行状态进入堵塞状态),它使当前线程在指定时间内,将运行机会给其它线程,但不会释放对象锁。wait 是Object 类的方法。3,此对象调用wait(方调用后会马上释放对象锁。线程挂起也就是说wait以下的程序都不再运行,当其它线程对此对象调用notify后才会运行) 方法导致本线程释放对象锁。仅仅有针对此对象发出notify(或notifyAll)方法后本线程才再次试图获得对象锁并进入运行状态。须要注意notify后不是立即就释放对象锁的,而是在对应的synchronized(){}语句块运行结束。自己主动释放锁后。JVM会在wait()对象锁的线程中随机选取一线程。赋予其对象锁。唤醒线程,继续运行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()Object.wait()二者都能够暂停当前线程,释放CPU控制权,基本的差别在于Object.wait()在释放CPU同一时候,释放了对象锁的控制。

    二 实际案例分析

      2.1案例源代码

    package KFC;

    public class Ham {

      static Objectbox=new Object();//监控对象

      static int totalmaterial=10;//可用的材料

      static int sales=0;//销售的个数

      static int production=5;//总共生产了多少个

    }

    package KFC;

     

    public class Hassistant implements Runnable {

        public void sell() {

          

    /*注意 这里必须是先加锁 然后再推断。假设是先推断,后加锁,则可能出现异步(和Hmaker同一时候运行的情况),会导致这里的推断结果不准确。以后在敲代码时也应当注意这点。都是先加锁再进行推断。

       下例是错误的,现进行说明:

    1.  先推断了Ham.production ==Ham.sales 是否成立,如果成立

    2.  这时假设Hmaker也运行,production++,改变了production的数量,注意这时还有汉堡。

    可是此时Hassistant方法仍然运行了synchronized (Ham.box)操作,告诉顾客已经没汉堡了。导致与实际情况相矛盾。

               if (Ham.production ==Ham.sales) {

    synchronized (Ham.box) {

                  System.out.println("营业员-" +this.getClass().getName()

                        + ":顾客朋友。汉堡没了。请稍等。

    ");

                  try {

                     Ham.box.wait();//此时整个线程会挂起 if语句外面的也不在运行

                     System.out.println("-----线程挂起-------");

                  } catch (Exception e) {

                     e.printStackTrace();

                  }

               }

            }

     

    */

               synchronized (Ham.box) {

                  if (Ham.production ==Ham.sales) {

                      System.out.println("营业员-" +this.getClass().getName()

                             + ":顾客朋友,汉堡没了,请稍等。。

    ");

                      try {

                        System.out.println("-----线程挂起-------");

                         Ham.box.wait();//此时整个线程会马上被挂起  Ham.box.notify()之前。wait()下的语句都不会再运行

                           System.out.println("营业员又一次获取对对象锁");  

                      } catch (Exception e) {

                         e.printStackTrace();

                      }

                  }

               }

     

       

           Ham.sales++;

           System.out.println("营业员-:顾客朋友汉堡来了,总共卖了:" + (Ham.sales) + "个,还剩"

                  + (Ham.production - Ham.sales) +"个,总生产了" + Ham.production

                  + "");

          

        }

     

        @Override

        public void run() {

           while (Ham.production <= Ham.totalmaterial) {

               try {

                  sell();

                  Thread.sleep(1000);// 1s卖一个

               } catch (InterruptedException e) {

                  e.printStackTrace();

               }

     

           }

     

        }

     

    }

    package KFC;

     

    import java.lang.*;

     

    public class Hmaker implements Runnable {//厨师

        public void make() {

           synchronized (Ham.box)//对资源进行加锁

           {

               (Ham.production)++;//汉堡数量添加

               System.out.println("厨师-" +this.getClass().getName()

                      +" 总共卖了:" + (Ham.sales)

                      + "个,还剩"+(Ham.production-Ham.sales)+"个,总生产了"+Ham.production+"");

               Ham.box.notify();//资源解锁 这里的解锁是指在同步块完毕后才真正的解锁 所以notify后 会继续运行

               System.out.println("--资源解锁了---");

           }

        }

     

        @Override

        public void run() {

           while (Ham.production <= Ham.totalmaterial)//还有材料

           {

                 

               try {

                  Thread.sleep(3000);// 3s做一个汉堡

               } catch (Exception e) {

                  e.printStackTrace();

               }

               this.make();

           }

     

        }

     

    }

     

    package KFC;

     

    public class ThreadTest {

    public static void main(String []args)

    {

        Hmaker maker=new Hmaker();

        Hassistant assistant=new  Hassistant();

        new Thread(assistant).start();

        new Thread(maker).start();

       

    }

    }

    2.2源代码分析

    代码运行步骤例如以下:

    时间

    Hmaker

    Hassistant

    production

    sales

    left

    1s

     

    销售1个

    5

    1

    4

    2s

     

    销售1个

    5

    2

    3

    3s

     

    销售1个

    5

    3

    2

    4s

    生产1个,此时尽管运行了notify方法,没有线程处在wait()对象锁的线程

    销售1个

    6

    4

    2

    5s

     

    销售1个

    6

    5

    1

    6s

     

    销售1个

    6

    6

    0

    7s

     

    此时,因为盒子里已经没有汉堡,因此运行wait()方法,释放资源锁,线程挂起进入block状态

     

     

     

    8s

    生产了一个,运行notify方法,此时,处在wait()对象锁状态的仅仅有Hassistant线程一个。

    由block状态先进入runnable状态,获取cpu时间,进入running状态,销售一个

    7

    7

    0

    9s

     

    此时,因为盒子里已经没有汉堡,因此运行wait()方法。释放资源锁,线程挂起进入block状态

     

     

     

    10s

    ….

    11s

     

     

     

     

     

    12s

     

     

     

     

     

    13s

     

     

     

     

     

    14s

     

     

     

     

     

    15s

     

     

     

     

     

    16s

     

     

     

     

     

    17s

     

     

     

     

     

    18s

     

     

     

     

     

    19s

     

     

     

     

     

    20s

     

     

     

     

     

    21s

     

     

     

     

     

    22s

     

     

     

     

     

    23s

     

     

     

     

     

     

     

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    Linux编程 3 (初识bash shell与man查看手册)
    Linux编程 2 (遍历目录cd与查看文件和目录ls)
    Linux编程 1 (文件系统路径说明, 目录结构说明)
    sql server 备份与恢复系列八 系统数据库备份与恢复分析
    mysql 开发进阶篇系列 34 工具篇 mysqlcheck(MyISAM表维护工具)
    mysql 开发进阶篇系列 33 工具篇(mysqlbinlog日志管理工具)
    mysql 开发进阶篇系列 32 工具篇(mysqladmin工具)
    (网页)理解Angular中的$apply()以及$digest()
    (后台)El表达式格式化两位小数
    (网页)Http状态码大全(200、404、500等)(转CSDN)
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4734345.html
Copyright © 2020-2023  润新知