zoukankan      html  css  js  c++  java
  • Java基础Day08(多线程)

    多线程

    1. 线程

    1.1 什么是线程:

      程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列或者说是进程的子任务)

      多线程执行时,在栈内存中,每一个执行线程都有自己所属的栈内存空间。进行方法的压栈和弹栈。

    1.2 流程图:

    1.3 自定义多线程:

    public class MyThread extends Thread{
    /*
    * 利用继承中的特点
    * 将线程名称传递 进行设置
    */
      public MyThread(String name){
      super(name);
    }
    /*
    * 重写run方法
    * 定义线程要执行的代码
    */
      public void run(){
      for (int i = 0; i < 20; i++) {
      //getName()方法 来自父亲
      System.out.println(getName()+i);
        } 
      }
    }
        //测试类
        public class Demo {
        public static void main(String[] args) {
          System.out.println("这里是main线程");
          MyThread mt = new MyThread("小强");
          mt.start();//开启了一个新的线程
            for (int i = 0; i < 20; i++) {
              System.out.println("旺财:"+i);
              }
            }
          }

      

    1.4 并发与并行

    • 并发:指两个或多个事件在同一个时间段内发生。
    • 并行:指两个或多个事件在同一时刻发生(同时发生)。
      注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同
      理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个
      线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为
      线程调度。

    1.5 线程与进程

      进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多
    个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创
    建、运行到消亡的过程。
      线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程
    中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
     

    1.6 线程调度:

    • 分时调度 : 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
    • 抢占式调度 : 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。设置线程的优先级
     

    1.7 抢占式调度详解

      大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我
    们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是
    在同时运行,”感觉这些软件好像在同一时刻运行着“。
      实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而
    言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是
    在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的
    使用率更高。
     

    1.8  Thread类

    API中该类中定义了有关线程的一些方法,具体如下:

    1.9 构造方法:

    • public Thread() :分配一个新的线程对象。
    • public Thread(String name) :分配一个指定名字的新的线程对象。
    • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
    • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

    1.10 常用方法:

    • public String getName() :获取当前线程名称。
    • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
    • public void run() :此线程要执行的任务在此处定义代码。
    • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

    1.11 创建线程类

      Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。
    Java中通过继承Thread类来创建并启动多线程的步骤如下:
      1. 定义Thread类的子类,并重写该类的run()方法该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
      2. 创建Thread子类的实例,即创建了线程对象
      3. 调用线程对象的start()方法来启动该线程
     
    测试类
    public class Demo01 {
       public static void main(String[] args) { 
      //创建自定义线程对象 
      MyThread mt = new MyThread("新的线程!"); 
      //开启新线程 
      mt.start(); //在主方法中执行for循环
      for (int i = 0; i < 10; i++) { 
        System.out.println("main线程!"+i);
         } 
      }
    }
    
    自定义线程类:
    public class MyThread extends Thread { 
      //定义指定线程名称的构造方法 
      public MyThread(String name) {  //调用父类的String参数的构造方法,指定线程的名称  super(name);
      /**
      * 重写run方法,完成该线程执行的逻辑
      */
      @Override
      public void run() {
        for (int i = 0; i < 10; i++) {
        System.out.println(getName()+":正在执行!"+i);
        }
       }
      }
    }

    1.12 创建线程方式二

    步骤如下:

      1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
      2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
      3. 调用线程对象的start()方法来启动线程
     
    public class MyRunnable implements Runnable{
    @Override
    public void run() {
    for (int i = 0; i < 20; i++) {
    System.out.println(Thread.currentThread().getName()+" "+i);
    } } }
    public class Demo {
    public static void main(String[] args) {
    //创建自定义类对象 线程任务对象
    MyRunnable mr = new MyRunnable();
    //创建线程对象
    Thread t = new Thread(mr, "小强");
    t.start();
    for (int i = 0; i < 20; i++) {
    System.out.println("旺财 " + i);
    } } }
    

      

    • 通过实现Runnable接口,使得该类有了多线程类的特征。
    • run()方法是多线程程序的一个执行目标。
    • 所有的多线程代码都在run方法里面。
    • Thread类实际上也是实现了Runnable接口的类。

    1.13  Thread和Runnable的区别

      如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

    1.14  实现Runnable接口比继承Thread类所具有的优势:

      1. 适合多个相同的程序代码的线程去共享同一个资源。
      2. 可以避免java中的单继承的局限性。
      3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
      4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
      扩充:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用命Java令执行一个类的时候,都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。

    1.15  匿名内部类方式实现线程的创建

      使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
      使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:
    public class NoNameInnerClassThread {
    public static void main(String[] args) {
    // new Runnable(){
    // public void run(){
    // for (int i = 0; i < 20; i++) {
    // System.out.println("张宇:"+i);
    // }
    // }
    // };     //‐‐‐这个整体 相当于new MyRunnable()
    Runnable r = new Runnable(){
    public void run(){
    for (int i = 0; i < 20; i++) {
    System.out.println("张宇:"+i);
    } }
    };
    new Thread(r).start();
    for (int i = 0; i < 20; i++) {
    System.out.println("费玉清:"+i);
    } } }
    

    2.1  线程安全

      如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
     

    2.2 线程同步

      使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
     
    •  同步代码块。

      • synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
        • 格式:synchronized(同步锁){需要同步操作的代码}
        • /*
          * 执行卖票操作
          */
          @Override
          public void run() {
          //每个窗口卖票的操作
          //窗口 永远开启
          while(true){
          synchronized (lock) {
          if(ticket>0){//有票 可以卖
          //出票操作
          //使用sleep模拟一下出票时间
          try {
          Thread.sleep(50);
          } catch (InterruptedException e) {
          // TODO Auto‐generated catch block
          e.printStackTrace();
          }
          //获取当前线程对象的名字
          String name = Thread.currentThread().getName();
          System.out.println(name+"正在卖:"+ticket‐‐);
          } } }
          
    •  同步方法。

      • 同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
        • 格式: public synchronized void method(){可能会产生线程安全问题的代码}
        • /*
          * 执行卖票操作
          */
          @Override
          public void run() {
          //每个窗口卖票的操作
          //窗口 永远开启
          while(true){
          sellTicket();
          } }
          /*
          * 锁对象 是 谁调用这个方法 就是谁
          * 隐含 锁对象 就是 this
          *
          */
          public synchronized void sellTicket(){
          if(ticket>0){//有票 可以卖
          //出票操作
          //使用sleep模拟一下出票时间
          try {
          Thread.sleep(100);
          } catch (InterruptedException e) {
          // TODO Auto‐generated catch block
          e.printStackTrace();
          }
          //获取当前线程对象的名字
          String name = Thread.currentThread().getName();
          System.out.println(name+"正在卖:"+ticket‐‐);
          } } }
          
    •  锁机制。

      • java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。Lock锁也称同步锁,加锁与释放锁方法化了。
        • public void lock() :加同步锁。
        • public void unlock() :释放同步锁 
        • Lock lock = new ReentrantLock();
          /*
          * 执行卖票操作
          */
          @Override
          public void run() {
          //每个窗口卖票的操作
          //窗口 永远开启
          while(true){
          lock.lock();
          if(ticket>0){//有票 可以卖
          //出票操作
          //使用sleep模拟一下出票时间
          try {
          Thread.sleep(50);
          } catch (InterruptedException e) {
          // TODO Auto‐generated catch block
          e.printStackTrace();
          }
          //获取当前线程对象的名字
          String name = Thread.currentThread().getName();
          System.out.println(name+"正在卖:"+ticket‐‐);
          }
          lock.unlock();
          } } }
          

            

    2.3  线程状态

    线程状态
     
              导致状态发生条件
        NEW(新建)       线程刚被创建,但是并未启动。还没调用start方法。
    Runnable(可运行)
     
    线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
    Blocked(锁阻塞)
    当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
    Waiting(无限等待)
    一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
    TimedWaiting(计时等待)
     
    同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
    Teminated(被终止)
    因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    搭建本地yum仓库
    linux下查看http 并发和 tcp连接数
    MySQL用户管理及SQL语句详解
    API开发之接口安全(一)----生成sign
    TP5使用API时不可预知的内部异常
    TP5通用化API接口数据封装
    根据指定日期获取近一周,及该月起止时间戳
    14-Promise
    4-字符串扩展与新增方法
    换行
  • 原文地址:https://www.cnblogs.com/ZhuX-H/p/13227079.html
Copyright © 2011-2022 走看看