创建进程的方式

  • 继承Thread类创建线程类
    1. 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
    2. 创建Thread子类的实例,即创建了线程对象。
    3. 调用线程对象的start()方法来启动该线程。
  • 通过Runnable接口创建线程类
    1. 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
    2. 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
    3. 调用线程对象的start()方法来启动该线程。
  • 通过Callable和Future创建线程
    1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
    2. 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
    3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。
    4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

线程的状态

  • 创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
  • 就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
  • 运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
  • 阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
  • 死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪

并行和并发的区别

  • 并行是指两个或者多个事件在同一时刻发生;
  • 并发是指两个或多个事件在同一时间间隔发生。
  • 并行是在不同实体上的多个事件
  • 并发是在同一实体上的多个事件

synchronized

  • 被它修饰的方法使其只能每次被单一的线程存取
  • 修饰方法、对象、代码块

synchronized 和 volatile 的区别

  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

synchronized 和 Lock 的区别

  • 首先synchronized是java内置关键字,在jvm层面,Lock是个java类
  • synchronized无法判断是否获取锁的状态Lock可以判断是否获取到锁
  • synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  • 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了
  • Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
  • synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);

线程和进程

  • 进程是资源分配的最小单元,线程是CPU调度的最小单元

    进程=火车 线程=车厢

  • 线程在进程下进行

  • 一个进程可以包含多个线程

  • 不同进程间数据很难共享

    简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。


守护线程

  • 守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程。

runable和callable的区别

  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

sleep和wait的区别

sleep和wait

  • sleep():方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象
  • wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

线程的 run()和 start()的区别

每个线程都是通过某个特定target对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
start()方法来启动一个线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码; 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。
run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。


线程池


java程序中怎么保证多线程的运行安全

  • 线程安全在三个方面体现:
    • 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作
    • 可见性:一个线程对主内存的修改可以及时地被其他线程看到,一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序
    • 有序性:指程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的

多线程锁的升级原理是什么

  • 在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级
  • 锁升级状态 详解各种锁
    锁升级状态

Java的锁

  • Java中的各种锁详细介绍
    Java锁
  • 乐观锁 VS 悲观锁
  • 自旋锁 VS 适应性自旋锁
  • 无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁
  • 公平锁 VS 非公平锁
  • 可重入锁 VS 非可重入锁
  • 独享锁 VS 共享锁#

什么是死锁

  • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。是操作系统层面的一个错误,是进程死锁的简称,

  • 死锁产生的四个必要条件:

    • 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
    • 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
    • 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放

    *环路等待条件:是指进程发生死锁后,若干进程之间形成一种头尾相接的循环等待资源关系

  • 如何防止死锁
    * 参照产生的四个必要条件,只要不满足其一,就可防止死锁


ThreadLocal

  • 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

  • 详解ThreadLocal


  • Servlet是线程不安全的,在Servlet类中可能会定义共享的类变量,这样在并发的多线程访问的情况下,不同的线程对成员变量的修改会引发错误

同步和异步

  • 同步:如果数据在线程间共享,例如正在写的数据可能被另外一个线程读到,或者正在读的数据可能被另外一个线程写过了,那么这些数据就是共享数据,必须同步存取
  • 异步:当应用程序在对象上调用了一个需要花很长时间来执行的方法,并且不希望让程序等待方法的返回是,就应该使用异步编程,往往异步编程更有效率。

并行和并发

  • 并行是指多个事件在同时刻发生,并发是指多个事件在同一时间间隔内发生

  • 并行是在不同实体上的多个事件,并发是指在同一实体上的多个事件

  • 并发编程的目的是充分利用处理器的每一个核,以达到最高的处理性能。


参考:https://blog.csdn.net/fangchao2011/article/details/89184943