zoukankan      html  css  js  c++  java
  • 线程基础总结

    线程基础

    线程表示一条单独的执行流,它有自己的程序执行计数器,有自己的栈

    java中线程的实现方式:

    • 继承Thread类并重写其run方法(Thread类也实现了Runnable接口,并提供了默认实现),调用Thread类的实例方法start()启动线程
    • 实现Runnable接口(重写run()方法),传递Runnable对象给Thread对象,调用Thread类的实例方法start()启动线程

    需注意的点:

    • run方法的方法签名是固定的,public,没有参数,没有返回值,不能抛出受检异常。run方法类似于单线程程序中的main方法,线程从run方法的第一条语句开始执行直到结束。
    • start表示启动该线程,使其成为一条单独的执行流,背后,操作系统会分配线程相关的资源,每个线程会有单独的程序执行计数器和栈,操作系统会把这个线程作为一个独立的个体进行调度,分配时间片让它执行,执行的起点就是run方法。
    • 如果直接调用run方法就是执行一个普通方法,并不会开辟新的线程。
    • Thread有一个静态方法currentThread,可以返回当前执行的线程对象

    线程基本属性和方法

    id和name:

    id是一个递增的整数,每创建一个线程就加一,name的默认值是"Thread-"后跟一个编号。name通过构造方法或者实例方法setName(String name)设置

    优先级(priority):

    在Java中,线程的优先级从1到10,默认为5,可通过实例方法getPriority()和setPriority(int newPriority)查看或设置

    需注意:这个优先级会被映射到操作系统中线程的优先级,不过,因为操作系统各不相同,不一定都是10个优先级,Java中不同的优先级可能会被映射到操作系统中相同的优先级,另外,优先级对操作系统而言更多的是一种建议和提示,而非强制,简单的说,在编程中,不要过于依赖优先级

    状态:

    线程有个实例方法getState()返回枚举类型Thread.State的状态,如下:

    public enum State {
      NEW,//没有调用start的线程状态为NEW
      RUNNABLE,//调用start后线程在执行run方法且没有阻塞时状态为RUNNABLE,不过,RUNNABLE不代表CPU一定在执行该线程的代码,可能正在执行也可能在等待操作系统分配时间片,只是它没有在等待其他条件
      BLOCKED,//线程被阻塞,位于锁等待队列在等待锁
      WAITING,//线程被阻塞,位于条件等待队列在等待被notify
      TIMED_WAITING,//线程被阻塞,位于条件等待队列等待被notify或者超时
      TERMINATED;//线程运行结束后状态为TERMINATED
    }

    可以调用Thread的实例方法isAlive(),查看线程是否活着:线程被启动后,run方法运行结束前,返回值都是true。

    是否daemo线程

    可通过实例方法setDaemon(boolean on)和isDaemon()设置或查看线程是否是守护线程。

    守护线程:一般是其他线程的辅助线程,在它辅助的主线程退出的时候,它就没有存在的意义了。所以当整个程序中剩下的都是daemo线程的时候,程序就会退出。java中负责垃圾回收的线程就是典型的守护线程

    sleep方法

    Thread的一个静态方法,调用sleep(long millis)方法会让当前线程睡眠指定的时间,睡眠期间线程会让出cpu。可中断,会抛出InterruptedException

    yield方法

    Thread的一个静态方法,调用yield()方法是告诉操作系统的调度器自己可以让出cpu,但是调度器可能忽略

    join方法

    Thread有一个实例方法join(),可以让调用join的线程等待该线程结束.它有个变体,可传入最长等待时间

    共享内存及问题

    共享内存

    每个线程表示一条单独的执行流,有自己的程序计数器,有自己的栈,但是不同线程之间可以共享堆区和方法区的内容,比如可以共享同一个对象或类的静态属性

    竞态条件

    竞态条件(race condition)是指,当多个线程访问和操作同一个对象时,最终执行结果与执行时序有关,可能正确也可能不正确。

    举例:对非原子操作i=i+1来讲,当一个线程取得i原来的值(假设为1)+1后还没来得及再次赋给i,就被另一个线程取到了i未+1之前的值(1)再次进行+1,最后导致了我们期望结果为3但却是2的问题。

    内存可见性

    在计算机系统中,除了内存,数据还会被缓存在CPU的寄存器以及各级缓存中,当访问一个变量时,可能直接从寄存器或CPU缓存中获取,而不一定到内存中去取,当修改一个变量时,也可能是先写到缓存中,而稍后才会同步更新到内存中。在单线程的程序中,这一般不是个问题,但在多线程的程序中,尤其是在有多CPU的情况下,这就是个严重的问题。一个线程对内存的修改,另一个线程看不到,一是修改没有及时同步到内存,二是另一个线程根本就没从内存读

    (此时volatile可以登场了,当然锁也可以)

     

    线程的优点及成本

    优点

    • 充分利用多CPU的计算能力,单线程只能利用一个CPU,使用多线程可以利用多CPU的计算能力。
    • 充分利用硬件资源,CPU和硬盘、网络是可以同时工作的,一个线程在等待网络IO的同时,另一个线程完全可以利用CPU,对于多个独立的网络请求,完全可以使用多个线程同时请求。
    • 在用户界面(GUI)应用程序中,保持程序的响应性,界面和后台任务通常是不同的线程,否则,如果所有事情都是一个线程来执行,当执行一个很慢的任务时,整个界面将停止响应,也无法取消该任务。
    • 简化建模及IO处理,比如,在服务器应用程序中,对每个用户请求使用一个单独的线程进行处理,相比使用一个线程,处理来自各种用户的各种请求,以及各种网络和文件IO事件,建模和编写程序要容易的多

    成本

    创建线程需要消耗操作系统的资源,操作系统会为每个线程创建必要的数据结构、栈、程序计数器等,创建也需要一定的时间。

    当线程足够多的时候,操作系统忙于上下文切换(分配cpu时间片,保存程序计数器和栈中的数据到内存等),这个切换不仅耗时,而且使CPU中的很多缓存失效。

  • 相关阅读:
    hdu 4614 线段树 二分
    cf 1066d 思维 二分
    lca 最大生成树 逆向思维 2018 徐州赛区网络预赛j
    rmq学习
    hdu 5692 dfs序 线段树
    dfs序介绍
    poj 3321 dfs序 树状数组 前向星
    cf 1060d 思维贪心
    【PAT甲级】1126 Eulerian Path (25分)
    【PAT甲级】1125 Chain the Ropes (25分)
  • 原文地址:https://www.cnblogs.com/JackPn/p/9425496.html
Copyright © 2011-2022 走看看