zoukankan      html  css  js  c++  java
  • 太牛了!第一次看到有大佬把Java线程与进程的这么详细的,我先收藏了!

    线程与进程

    概念

    进程:进程指的是一段正在运行的程序
    线程:一个程序运行中可以执行多个任务,任务就称之为线程。
    进程可以有多个线程,各个线程之间共享程序的内存空间

    为什么有了进程,还会出现线程?

    每个进程有自己独立的地址空间,多并发请求,为每一个请求创建一个进程,导致系统开销、用户请求效率低

    区别

    每个进程有用自己独有的变量,线程共享数据,线程之间的通信相比于进程之间的通信更加有效,更加容易
    线程相比于进程创建/销毁开销 更小
    进程是资源分配的最小单位,线程是CPU调度的最小单位
    多进程程序更加健壮,多线程程序只要有一个线程挂掉,对其共享资源的其他线程也回产生影响
    如果追求速度,追求系统稳定选择线程;如果频繁的创建和销毁,选择线程;

    线程的创建

    说完线程的基本知识,那么久要谈谈在应用中,我们应该如何创建线程,实际应用中创建线程主要有以下四种方法,如下:

    方法一:继承Thread类,重写run()方法

    
    class  MyThread extends  Thread{
        @Override
        public void run() {
            while(true){
                System.out.println("eat food");
            }
        }
    }
    public class TestDemo1 {
        public static void main(String[] args) {
        	//边吃饭边看电视
            //创建子线程对象
            Thread thread = new MyThread();
            //启动吃饭的thread
             thread.run();
             //main线程
            while(true){
                System.out.println("watch Tv");
            }
        }
      }
    
    

    方法二:实现Runable接口

    class  MyRunnable implements  Runnable{
        @Override
        public void run() {
            while(true){
                System.out.println("eat food");
            }
        }
    }
    public class TestDemo1 {
        public static void main(String[] args) {
        //实现runnable接口 创建子线程  创建子线程对象        Thread thread = new Thread(new MyRunnable());	
        thread.start();
        }
      }
    
    

    方法三:匿名线程 使用匿名内部类

      //匿名内部类   创建线程
                new Thread(){
                    @Override
                    public void run() {
                     System.out.println("thread 0");
                    }
                }.start();
    
    

    方法四:实现Callable接口,重写call()方法

    利用Callable创建时,步骤较为复杂,具体如下:
    a.创建Callable接口的实现类,重写call方法
    b.创建Callable实现类的实例,使用FutureTask包装该实例
    c.将FutureTask实例作为参数创建线程对象
    d.启动该线程
    e.调用FutureTask的get方法获取子线程的执行结果

    class MyCallable implements Callable<Integer>{
    
        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for(int i=0; i<10000; i++){
                sum += i;
            }
            return sum;
        }
    }
    public class TestDemo1 {
        public static void main(String[] args) {
         //创建Callable接口的实现类    重写call 方法
            //创建Callable实现类的实例
            Callable<Integer> callableTask = new MyCallable();
            //使用FutureTask包装实例
            FutureTask<Integer> task = new FutureTask<>(callableTask);
            //Future实例作为参数创建线程对象
            Thread thread = new Thread(task);
            //启动该线程
            thread.start();
            //调用FutureTask的get 方法获取子线程的执行结果
            try {
                Integer integer = task.get();
                System.out.println("result"+integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
    

    可能有人会思考,这里同样使用Runable和Callable接口来创建线程,那么这两者各有什么不同呢?能想到这,可谓是很用心啦,这也是这里的一个面试考点呐!!!
    Runable 接口和 Callable 接口的区别?

    这里就简单说一下两者的区别,但是仅仅单独比较两个接口的区别实际意义并不大,并且也不是很全面,之后会
    详细的分析两个接口的区别,在更深层次上去理解两个接口。大家继续关注吖!

    线程的生命周期及常用方法解析

    生命周期

    线程的生命周期主要包括以下六种状态:新建状态、就绪状态、阻塞状态、等待状态、睡眠状态、终止状态。

      1、new() :新建状态  new关键字创建一个线程对象,它并不是出于执行状态 并未执行START方法启动线程
      2、runnable() :就绪状态   等待被执行  线程对象调用START方法,才是JVM中真正创建了一个线程,
      创建好的线程并不是一经启动,就会立即执行。该状态的所有线程会位于就绪线程池中,等待当前操作系统的资源,例如CPU,
      获得CPU的使用权。
      3、blocked(): 阻塞状态 等待一个监视器锁进入同步代码块或者同步方法,代码块和方法指某一时刻
      只能够有一个线程去执行,其他线程只能等待。
      4、Waiting(): 等待状态  Object.wait()/Thread.join()/LockSupport.park() 都会使得当前从Runnable
      转换为Waiting 状态    调用waiting()  会释放monitor Lock
      5、TimeWaiting():睡眠状态    
       调用Object.wait(long miles)/Thread.sleep(long miles)/LockSupport.parkNano()/LockSupport.parkUntile()
      6、Terminated():终止状态  是一个线程的最终状态  线程如果进入此状态,意味着该线程结束。
    

    简单了解线程的生命周期之后,那我们继续学习线程六状态之间的转换:

    常用方法解析

    1) start()

    启动一个线程,将线程添加到线程组中,线程状态会从New 状态转换到Runnable 状态,然后获取CPU之后进入Running状态执行run();

    2)sleep()

    静态方法 ,存在两个重载函数

     public static native void sleep(long millis)
     public static void sleep(long millis, int nanos)
    
    

    作用:sleep()方法使得当前线程进入睡眠状态并指定休眠时间,暂停执行;
    sleep()方法不会释放Monitor Lock 使用权;
    JDK1.5之后,引入枚举类TimeUnit ,其对sleep进行封装 直接使用从而省去时间换算的步骤,不用将时间转为ms,更加的方便使用。

    3)yield()

    public static native void yield();
    

    作用: 提醒CPU调度器 ,我当前的线程愿意放弃当前的CPU资源(属于启发式方法),如果当前CPU资源不紧张,会忽略这种提醒。
    yield()方法不会释放Monitor Lock 使用权;

    4)join()

    join() 一直等待
    join(long millis) 等待指定毫秒数
    join(long millis, int nanos) 等待指定毫秒数
    

    含义:在线程B中join某个线程A,会使得B线程进入等待,直到线程A结束生命周期,或者达到给定的时间,在这给定时间期间线程B会处于等待状态.

    5)wait()

    调用某个对象的wait()方法可以让当前线程阻塞

    6)notify()

    调用当前对象notify/notifyAll才能够唤醒这个对象所在的线程。

    7)notifyAll()

    将锁对象等待池中所有线程移动到锁标志等待池中。
    注意:使用这三个方法需要让当前线程拥有当前对象的monitor lock

    8)线程中断方法

    每个Java线程都会有一个中断状态位,程序可以检测这个中断状态位判读线程是否执行结束。有以下三种方法:
    interrupt()
    public void interrupt() 由线程对象调用,将中断位置置为true

    public void interrupt();
    
    

    如下方法能够使得当前线程进入阻塞状态,调用interrupt方法可以打断阻塞,因此这种方法被称之为可中断方法

    Object.wait()/wait(long)
    Thread.sleep(long)/TimUnit.XXX.sleep(long)
    Thread.join()/Thread.join(long)
    
    

    如果一个线程被interrupt,设置interrupt flag;如果当前线程正在执行可中断方法,调用interrupt方法,反而导致interrupt flag被清除.

    isInterrupted

    public boolean isInterrupted();
    
    

    实例方法,判断当前线程的中断状态位是否为true,判断进程是否被中断。

    interrupted()

    public static boolean interrupted();
    

    静态方法,调用interrupted会擦除中断状态位的标识,判断进程是否被中断

    interrupted和isInterrupted方法的区别:

    1)interrupted()是静态方法,isInterrupted()是实例方法;
    2)interrupted()会清空线程中断状态,isInterrupted()不会清空线程中断状态。

    最后

    欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结! 这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

  • 相关阅读:
    Web APIs
    变量替换
    用shell脚本实现文件、代码同步上线
    20.31 expect脚本同步文件 20.32 expect脚本指定host和要同步的文件 20.33 构建文件分发系统 20.34 批量远程执行命令
    20.27 分发系统介绍 20.28 expect脚本远程登录 20.29 expect脚本远程执行命令 20.30 expect脚本传递参数
    20.23/20.24/20.25 告警系统邮件引擎 20.26 运行告警系统
    20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析
    20.5 shell脚本中的逻辑判断 20.6 文件目录属性判断 20.7 if特殊用法 20.8/20.9 case判断
    16进制数据拆分
    16进制数据拼接成一个
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14449273.html
Copyright © 2011-2022 走看看