• 线程和进程


    线程和进程


    1. 线程和进程的概念,并行和并发的概念
      1、 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
      2、线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。进程内一个相对独立、可调度的执行单元,是系统独立调度和分派CPU的基本单位,也指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
      1、并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干端,使多个进程快速交替的执行。
      2、并行(parallellism):指在同一时刻,有多条指令在多个处理器上同时执行

    1. 创建线程的方式及实现

      Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。
      Java可以用三种方式来创建线程,如下所示:
      1)继承Thread类创建线程
      2)实现Runnable接口创建线程
      3)使用Callable和Future创建线程
      

      通过继承Thread类来创建并启动线程:

      1.定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是该线程需要完成的任务。
      2.创建Thread类的实例,也就是创建线程对象。
      3.启动线程,调用线程的start方法。
      public class MyThread extends Thread{
       public void run(){
           //重写run()方法
       }
      }
      public class Main{
       public static void main(String[] args){
           new MyThread().start();//创建并启动线程
       }
      }
      

      通过实现Runnable接口创建并启动线程一般步骤如下:

      1.定义Runnable接口的实现类,也要重写run()方法
      2.创建实现类的实例,并用这个实例作为Thread的参数来创建Thread对象,这个Thread对象才是真正的线程对象。
      3.调用线程对象的start()方法
      public MyThread2 implements Runable{
       public void run(){
           //重写run()方法
       }
      }
      public Class Main(){
       public static void mian(String[] args){
           MyThread2 myThread = new MyThread2();
           Thread thread = new Thread(myThread);
           thread().start();
           //或者 new Thread(new MyThread2()).start();
       }
      }
      

      使用Callable和Future创建线程。Callable接口提供了一个call()方法作为线程的执行体。call方法比run方法的功能要强大。
      使用案例

      》call()方法可以有返回值
      》call()方法可以声明抛出异常
      Java5提供了Future接口来代表Callable接口里call()方法的返回值,
      并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,
      还实现了Runnable接口,因此可以作为Thread类的target。
      在Future接口里定义了几个公共方法来控制它关联的Callable任务。
      >boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务
      >V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
      >V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
      >boolean isDone():若Callable任务完成,返回True
      >boolean isCancelled():如果在Callable任务正常完成前被取消,返回True
      

      创建并启动有返回值的线程的步骤如下:

      1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
      2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
      3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
      4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
      public class Main{
       public static void main(String[] args){
           MyThread3 th = new MyThread3();
           //使用Lambda表达式来创建Callable对象
           //使用FutureTask类来包装Callable对象
           FutureTask<Integer> future = new FutureTask<Integer>(
               (Callable<Integer>()->{
                   return 5;
               })
           );
      
           new Thread(future,"有返回值的线程").start();
           try{
               System.out.println("子线程的返回值:"future.get());
               //get()方法会阻塞,直到子线程执行结束才返回
           }catch(Exception e){
               e.printStackTrace();
           }
       }
      }
      

      参考链接


    1. 进程间的通信方式
      ```
      常见的通信方式:
    2. 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    3. 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    4. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    5. 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
    6. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    7. 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
    8. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
      ```

    1. 说说CountDownLatch,CyclicBarrier原理和区别
      CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
      CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
      原理参考链接
      代码参考

    1. 说说Semaphore原理
      信号量是一个非负整数,表示了当前公共资源的可用数目(在上面的例子中可以用空闲的停车位类比信号量),当一个线程要使用公共资源时(在上面的例子中可以用车辆类比线程),首先要查看信号量,如果信号量的值大于1,则将其减1,然后去占有公共资源。如果信号量的值为0,则线程会将自己阻塞,直到有其它线程释放公共资源。
      在Java的并发包中,Semaphore类表示信号量。
      源码分析

    1. 说说Exchanger原理
      Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会 一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。因此使用Exchanger的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的。
      Exchanger类提供了两个方法,String exchange(V x):用于交换,启动交换并等待另一个线程调用exchange;String exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange,并且设置最大等待时间,当等待时间超过timeout便停止等待。
      参考链接

    1. ThreadLocal原理分析,ThreadLocal为什么会出现OOM,出现的深层次原理
      原理

    8.讲讲线程池的实现原理
    线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:
    1、降低资源消耗;
    2、提高响应速度;
    3、提高线程的可管理性。
    Java1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。

    参考链接

    1. 线程池的几种实现方式
      Java通过Executors提供四种线程池,分别为:
      newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
      newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
      newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
      newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
      
      参考链接

    10.线程的生命周期,状态是如何转移的

    参考链接

    参考:java多线程编程核心技术

  • 相关阅读:
    今日头条 算法 架构
    什么才是真正的成长
    罗素 哲学 数学
    商业模式 广告 DSP
    人工智能 商业 落地 榜单
    【转】没有过时的CRM 图解大全
    20个人的初创公司,采用哪些技术栈和软件便于快速研发?
    【转】DevSecOps:打造安全合规的 DevOps 平台
    spring security HttpSessionEventPublisher & spring session HttpSessionListener
    JEECG codegenerate-3.6.3 maven
  • 原文地址:https://www.cnblogs.com/fruitknife/p/9703172.html
Copyright © 2020-2023  润新知