zoukankan      html  css  js  c++  java
  • Java提高——多线程(一)状态图

    操作系统中的进程和线程的概念
           进程是指一个内存运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程,比如windows下的一个运行的应用程序.exe就是一个进程。
           线程是指进程中的一个执行流,一个进程可以运行多个线程。如java.exe就可以运行很多个线程。线程总是属于某个进程,进程中的多个线程共享进程中的内存。
           操作系统中可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程。
       
    线程的状态


    1、新建(new):线程对象被创建后就进入了新建状态。如:Thread thread = new Thread();

    2、就绪状态(Runnable):也被称为“可执行状态”。线程对象被创建后,其他线程调用了该对象的start()方法,从而启动该线程。如:thread.start(); 处于就绪状态的线程随时可能被CPU调度执行。

    3、运行状态(Running):线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

    4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权限,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。阻塞的三种情况:

        1)等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。

        2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。

        3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。

    5、死亡状态(Dead):线程执行完了或因异常退出了run()方法,该线程结束生命周期。

    常用的实现多线程的方式

    1、继承Thread类

    public class Thread implements Runnable {、、、、、}

    由Thread的源码可以看出,Thread是实现了Runnable接口。

    2、实现Runnable接口

    public interface Runnable {
       
        public abstract void run();
    }

    Runnable接口只有一个run()方法

    ————————————————————————————————————————————————

    Thread和Runnable都是是实现实现多线程的方式。不同的是Thread是类,Runnable是接口——Thread本身实现了Runnable接口,一个类只能有一个父类,但是却可以是实现多个接口,因此Runnable具有更好的扩展性

    Thread实现多线程

    public class MyThread extends Thread {
        private int i ;
        @Override/** 重写run方法*/
        public void run(){
            for (int i = 0; i < 50 ; i++) {
                /**
                 * 当继承Thread类可以直接用this获取当前线程
                 * 用getName()获取当前线程的名字
                 */
                System.out.println(this.getName()+"-"+i);
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100 ; i++) {
                //通过Thread.currentThread()获取当前线程
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==20){
                    //创建并启动第一个线程
                    new MyThread().start();
                    //创建并启动第二个线程
                    new MyThread().start();
                }
            }
        }
    }

    Runnable实现多线程

    public class MyRunnable implements Runnable {
        private int i;
    
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                /**当线程类实现Runnable接口的时候,获取当前线程只能用Thread.currentThread()*/
                System.out.println(Thread.currentThread().getName()+" "+i);
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
               // System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==20){
                    MyRunnable mr = new MyRunnable();
                    //通过new Start()方法创建新线程
                    new Thread(mr,"线程1").start();
                    new Thread(mr,"线程2").start();
                }
    
            }
        }
    }
    
    关于线程中的start()和run()

    启动线程使用start(),而不是run()!


    在执行start()方法之前,只是有一个Thread对象,还没一个真正的线程。(分配内存,初始化成员变量

    ——>start()之后,线程状态从新状态到可执行状态。(调用栈和计数器,线程没运行,只是可以运行

    ——>当线程获得执行机会时,其目标run()方法将运行。

    start():他的作用是启动一个新线程,新线程会调用相应的run()方法。start()不能被重复调用。

    run():和普通成员的方法一样可以被重复调用。单独调用run()会在当前线程中执行run(),而不会启动新的线程。

    public class MyThread extends Thread {
        @Override/** 重写run方法*/
        public void run(){
           
            }
        }
    MyThread myThread = new MyThread(); 
    

    如:myThread.start()会启动一个新的线程,然后在新线程中执行run()方法。

    myThread.run()会直接在当前线程中运行run()方法,不会启动一个线程。

    start方法源码:

     public synchronized void start() {
         /**
        *如果线程不是就绪状态就抛出异常
        */
         if (threadStatus != 0)
             throw new IllegalThreadStateException();
    
         /* 将线程添加到group当中 */
         group.add(this);
    
         boolean started = false;
         try {
             start0();//通过start0启动线程
             started = true;//设置started标记
         } finally {
             try {
                 if (!started) {
                     group.threadStartFailed(this);
                 }
             } catch (Throwable ignore) {
                 /* do nothing. If start0 threw a Throwable then
                   it will be passed up the call stack */
             }
         }
     }
    

    run方法源码:

    public void run() {
        if (target != null) {
            target.run();
        }
    }

    其中target是Runnable对象, 直接调用Thread线程中Runnable接口中run方法,不会新建一个线程。

    当线程获得CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。

    关于阻塞


    进入阻塞的情况: 解除上面阻塞情况:

    1、线程调用sleep()方法主动释放占用的处理器资源

    2、调用了一个阻塞式的IO方法,在方法返回前线程被阻塞

    3、线程获得了一个同步监视器(Syschronized),但该监视器正被其他线程持有

    4、线程在等待某个通知(notify)

    5、程序调用了线程的resume()方法将线程挂起。但是该方法容易发生死锁,所以尽量避免使用

     

    1、调用sleep()方法经过了指定的时间

    2、线程调用的IO阻塞方法已经返回

    3、线程成功的获得了试图取得的同步监视器

    4、线程正在等待某个通知时,其他线程发出了一个通知

    5、处于挂起的线程被调用了resume()恢复方法。

    线程从阻塞状态只能进入就绪状态,无法进入运行状态,而从就绪到运行不受程序控制,由系统线程调度决定。

    获得资源进入运行状态,失去资源进入就绪状态。

    线程死亡


    线程死亡会以一下三种状况结束:

    —>调用run或call方法执行完成,正常结束

    —>线程抛出一个为捕获的Exception或Error

    —>直接调用stop()方法来结束该线程——容易导致死锁,不推荐


    不要试图对死亡的线程使用start()方法,将会抛出异常,并不会重启死亡的异常!

  • 相关阅读:
    html页面模板布局内容的继承,block
    url分发
    显示年月,注册页面和后台数据交互,不涉及数据库
    static文件夹中文件引用方式,如html页面引用js
    pycharm写django之返回一个页面
    pycharm编写django第一步
    VUE清除keepalive页面缓存
    js设置html根节点的style字体【Vue动态调整全局字体大小】
    npm 依赖重新安装或更新版本
    antd 自定义表头slots.title不生效
  • 原文地址:https://www.cnblogs.com/huangzhe1515023110/p/9276097.html
Copyright © 2011-2022 走看看