Java 线程入门知识总结

定义线程

  1. 定义一个实现 Runnable 接口的类 A, 并实现该接口中的 run 方法, 最后实例化类 A, 做为 Thread 类的构造参数。
// 定义一个实现 Runnable 接口的类 A,  并实现该接口中的 run 方法
public class A implements Runnable {
    [@Override](https://my.oschina.net/u/1162528)
    public void run() {
        System.out.println("开启一个县城... ");
    }
}

// 实例化类 A
A task = new A();

// 做为 Thread 类的构造参数。 
Thread t = new Thread(task);
  1. 定义一个继承 Thread 类的类 A, 并重写类 A 的 run 方法

注意: 其实仍是 Runnable 接口中 run 方法,由于 Thread 类实现了 Runnable接口java

// 定义一个继承 Thread 类的类 A,  重写类 A 的 run 方法
public class A extends Thread {
    [@Override](https://my.oschina.net/u/1162528)
    public void run() {
        System.out.println();
    }
}

Thread t = new Thread();

其余:经过线程池 或者 实现Callable接口的方法。 暂时很少作介绍安全

经过实现 Runnable 接口与继承 Thread 类定义线程的区别

  • Runnbale 接口的实现类必须重写 run() 方法, 而 Thread类 的实现类有默认实现的run方法多线程

  • Runnbale 接口的实现类并非真正的线程类, 他只是执行线程的一个任务类jvm

  • Thread 类要想以线程的方式执行run方法,只能依靠Thread类ide

  • 继承 Thread 类会形成单继承的局限性函数

线程的生命周期

线程的生命周期指的是从线程的建立到启动直到结束。this

java 线程的 6 种状态操作系统

注意: 这里说的是 java 线程的状态,并不是操做系统的线程状态.net

Thread 类中经过枚举定义了线程的状态线程

public enum State {
   
    NEW,

    RUNNABLE,

    BLOCKED,

    WAITING,
  
    TIMED_WAITING,

    TERMINATED;
}
  • New:new Thread() 后线程的状态就是新建。

  • Runnable:线程一旦调用 start() 方法,不管是否运行,状态都为 Runable, Runable 状态指示表示线程能够运行,不表示线程当下必定在运行,线程是否运行由虚拟机所在操做系统调度决定。

  • BLOCKED:线程试图获取一个内部对象的 Monitor(进入synchronized方法或synchronized块)可是其余线程已经抢先获取,那此线程被阻塞,知道其余线程释放 Monitor 而且线程调度器容许当前线程获取到 Monitor,此线程就恢复到可运行状态。

  • WAITING:当一个线程等待另外一个线程通知调度器一个条件时,线程进入等待状态。

  • TIMED_WAITING:和等待相似,某些形成等待的方法会容许传入超时参数,这类方法会形成计时等待,收到其余线程的通知或者超时都会恢复到可运行状态。

  • TERMINATED:线程执行完毕正常结束或执行过程当中因未捕获异常意外终止都会是线程进入被终止状态

扩展

操做系统中线程的5中状态

  • 新建
  • 就绪
  • 运行
  • 阻塞
  • 终止

线程调度

操做系统会给每一个线程分配时间片, 在某一时刻只执行一个时间片的线程。每一个java程序启动后, jvm会自动帮咱们建立两个线程, 一个是main, 一个是GC

线程调度的实现方式

  • 分时调度模型

    让全部线程轮流得到CPU的控制权,而且为每一个线程平均分配CPU时间片断

  • 抢占式调度模型

    选择优先级相对较高的线程执行,若是全部线程的优先级相同,则随机选择一个线程执行 Java虚拟机采用此种调度模型。

java 线程分类

用户线程

也称非守护线程, jvm会在全部非守护线程结束后随之离开

守护线程

也称做后台线程, 当进程中的全部 非后台线程 结束后, 后台线程也会随之结束。

  • 如何设置后台线程

    在线程启动前调用setDaemon()方法

  • 为何GC线程是守护线程?

    由于当全部的非守护线程结束后, 也就不会产生垃圾, 那么GC线程也就没有存在的意义

线程安全问题

出现线程不安全的条件

  1. 必须存在两个或者两个以上的线程。

  2. 多个线程共享着一个资源,并且操做资源的代码有多句。

解决办法

1. 同步代码块

使用 synchronized 修饰代码块, 以下所示

public  void methodA() {
    synchronized (this){
        // doSomething
    }
}

注意的事项:

  • 锁对象能够是任意的一个对象。
  • 锁对象必须是多个线程共享 的资源。
  • 调用了sleep方法的线程并不会释放锁对象。
  • 若是不存在着线程安全问题,千万不要使用同步代码块或者是同步函数, 由于会下降效率的。

2. 同步函数

使用synchronized修饰该函数则称做为同步函数, 以下所示。

public synchronized void methodA() {
    // doSomething        
}

注意的事项:

  • 非静态同步函数的锁对象是this对象,静态函数的锁对象是当前所属类的class文件对象。
  • 同步函数的锁对象是固定的,没法更改。

推荐使用: 同步代码块

  1. 同步代码块的锁对象能够由咱们本身指定,同步函数的锁对象是固定的。
  2. 同步代码块能够随意指定那个范围须要被同步,而同步函数必须是整个函数都同步, 代码不灵活。

死锁

死锁出现的条件

  • 必须存在两个及两个以上的线程
  • 这些线程共享两个及两个以上的资源
  • 多线程各自持有不一样的锁,并试图获取对方已持有的锁

如何解决

线程安全问题解决后,会出现死锁问题,死锁问题没法彻底解决,只能尽可能去避免死锁出现的条件

相关文章
相关标签/搜索