线程和进程

线程和进程


  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多线程编程核心技术

posted @ 2018-09-25 20:12  一把水果刀  阅读(157)  评论(0编辑  收藏  举报