Java多线程学习笔记

做者:Greyhtml

原文地址:Java多线程学习笔记java

源码: Githubgit

什么是程序,进程和线程?

  • 程序是计算机的可执行文件
  • 进程是计算机资源分配的基本单位
  • 线程是资源调度执行的基本单位
    • 一个程序里面不一样的执行路径
    • 多个线程共享进程中的资源

线程和进程的关系

线程就是轻量级进程,是程序执行的最小单位。github

多进程的方式也能够实现并发,为何咱们要使用多线程?编程

  1. 共享资源在线程间的通讯比较容易。
  2. 线程开销更小。

进程和线程的区别?

  • 进程是一个独立的运行环境,而线程是在进程中执行的一个任务。他们两个本质的区别是是否单独占有内存地址空间及其它系统资源(好比I/O)。
  • 进程单独占有必定的内存地址空间,因此进程间存在内存隔离,数据是分开的,数据共享复杂可是同步简单,各个进程之间互不干扰;而线程共享所属进程占有的内存地址空间和资源,数据共享简单,可是同步复杂。
  • 进程单独占有必定的内存地址空间,一个进程出现问题不会影响其余进程,不影响主程序的稳定性,可靠性高;一个线程崩溃可能影响整个程序的稳定性,可靠性较低。
  • 进程单独占有必定的内存地址空间,进程的建立和销毁不只须要保存寄存器和栈信息,还须要资源的分配回收以及页调度,开销较大;线程只须要保存寄存器和栈信息,开销较小。
  • 进程是操做系统进行资源分配的基本单位,而线程是操做系统进行调度的基本单位,即CPU分配时间的单位。

什么是线程切换?

从底层角度上看,CPU主要由以下三部分组成,分别是:网络

  • ALU: 计算单元
  • Registers: 寄存器组
  • PC:存储到底执行到哪条指令

T1线程在执行的时候,将T1线程的指令放在PC,数据放在Registers,假设此时要切换成T2线程,T1线程的指令和数据放cache,而后把T2线程的指令放PC,数据放Registers,执行T2线程便可。多线程

以上的整个过程是经过操做系统来调度的,且线程的调度是要消耗资源的,因此,线程不是设置越多越好。并发

单核CPU设定多线程是否有意义?

有意义,由于线程的操做中可能有不消耗CPU的操做,好比:等待网络的传输,或者线程sleep,此时就可让出CPU去执行其余线程。能够充分利用CPU资源。ide

  • CPU密集型
  • IO密集型

线程数量是否是设置的越大越好?

不是,由于线程切换要消耗资源。高并发

示例:
单线程和多线程来累加1亿个数。-> CountSum.java

工做线程数(线程池中线程数量)设多少合适?

  • 和CPU的核数有关

  • 最好是经过压测来评估。经过profiler性能分析工具jProfiler,或者Arthas

  • 公式

N = Ncpu * Ucpu * (1 + W/C)

其中:

  • Ncpu是处理器的核的数目,能够经过Runtime.getRuntime().availableProcessors() 获得

  • Ucpu是指望的CPU利用率(该值应该介于0和1之间)

  • W/C是等待时间和计算时间的比率。

Java中建立线程的方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,实现run方法,这比方式1更好,由于一个类实现了Runnable之后,还能够继承其余类
  3. 使用lambda表达式
  4. 经过线程池建立
  5. 经过Callable/Future建立(须要返回值的时候)

具体示例可见:HelloThread.java

线程状态

  • NEW

线程刚刚建立,尚未启动
即:刚刚New Thread的时候,尚未调用start方法时候,就是这个状态

  • RUNNABLE

可运行状态,由线程调度器能够安排执行,包括如下两种状况:

  • READY
  • RUNNING

READY和RUNNING经过yield来切换

  • WAITING

等待被唤醒

  • TIMED_WAITING

隔一段时间后自动唤醒

  • BLOCKED

被阻塞,正在等待锁
只有在synchronized的时候在会进入BLOCKED状态

  • TERMINATED

线程执行完毕后,是这个状态

线程状态切换

java_thread_state

interrupt

  • interrupt()

打断某个线程(设置标志位)

  • isInterrupted()

查询某线程是否被打断过(查询标志位)

  • static interrrupted

查询当前线程是否被打断过,并重置打断标志位

示例代码:ThreadInterrupt.java

如何结束一个线程

不推荐的方式

  • stop方法
  • suspend/resume方法

以上两种方式都不建议使用, 由于会产生数据不一致的问题,由于会释放全部的锁。

优雅的方式

若是不依赖循环的具体次数或者中间状态, 能够经过设置标志位的方式来控制

public class ThreadFinished {
    private static volatile boolean flag = true;
    public static void main(String[] args) throws InterruptedException {

        // 推荐方式:设置标志位
        Thread t3 = new Thread(() -> {
            long i = 0L;
            while (flag) {
                i++;
            }
            System.out.println("count sum i = " + i);
        });
        t3.start();
        TimeUnit.SECONDS.sleep(1);
        flag = false;
    }
}

若是要依赖循环的具体次数或者中间状态, 则能够用interrupt方式

public class ThreadFinished {

    public static void main(String[] args) throws InterruptedException {
        // 推荐方式:使用interrupt
        Thread t4 = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {

            }
            System.out.println("t4 end");
        });
        t4.start();
        TimeUnit.SECONDS.sleep(1);
        t4.interrupt();
    }
}

示例代码: ThreadFinished.java

参考资料

多线程与高并发-马士兵

实战Java高并发程序设计(第2版)

深刻浅出Java多线程

Java并发编程实战

相关文章
相关标签/搜索