JAVA多线程总结

来源:互联网 发布:奥拉星淘宝怎么买号 编辑:程序博客网 时间:2024/06/06 05:17

本文主要是学习了 老马说编程 的 计算机程序的思维逻辑中关于线程部分的知识,并对其进行回顾总结。

多线程的实现

  • Thread
    创建一个Thread的子类,并复写其run()方法,并在需要启动的地方调用其start方法
public class TestThread extends Thread {    @Override    public void run() {        System.out.println("I'm test thread");    }}public static void main(String[] args) {    Thread testThread = new TestThread();    testThread.start();}
  • Runnable
    创建一个实现了Runnable接口的类,并实现run方法,并在需要启动的地方创建一个Thread对象,并将此类作为参数传入,调用此thread对象的start方法。
public class TestThread implements Runnable{    @Override    public void run() {        System.out.println("I'm test thread");    }}public static void main(String[] args) {    Thread testThread = new Thread(new TestThread());    testThread.start();}

两者的区别在于Thread需要通过继承实现,而Runnable是通过接口,在单继承的java中,后者的使用应该更为便利。

线程的属性和方法

  • Thread.currentThread():返回当前线程对象
    • getName():获取线程名
    • getId():获取线程对应ID
  • 线程优先级:1~10,默认为5。优先级效果仅为建议非强制。
    • public final void setPriority(int newPriority) : 设置优先级
    • public final int getPriority() : 获取优先级
  • public State getState() : 获取线程状态
    • New : 未被start
    • TERMINATED : 运行结束
    • RUNNABLE : 正在执行run且未被阻塞
    • BLOCKED : 等待锁
    • WAITING : 等待状态
    • WAITING_TIME : 等待sleep时间结束
  • public final native boolean isAlive() : 线程是否存活,线程启动后且在run结束前,返回True
  • daemon线程 : 程序一般为辅助线程,当程序中仅剩下daemon线程时,程序会退出。例如 负责垃圾回收的线程
  • public final void setDaemon(boolean on) : 设置是否为Daemon线程
    • public final boolean isDaemon() : 返回该线程是否为Daemon线程
    • public static native void sleep(long millis) throws InterruptedException; : 该方法使当前线程睡眠millis时间(毫秒),当被中断时,sleep会抛出InterruptedException异常
  • public static native void yield(); : 让出CPU,仅为建议作用。
  • public final void join() throws InterruptedException : 调用线程等待该线程运行结束后再退出,可以被中断,当被中断时会抛出InterruptedException异常,可传入一个long参数,表示等待最长时间(毫秒)。

共享内存及问题

  • 共享内存:虽然每个线程都有自己的栈和程序计数器,但是它们可以访问和操作同一对象。
  • 竞态条件:当多个线程访问和操作同一对象时,最终执行结果和执行时序有关,不一样能得到预期结果。
    当两个线程同时取到了同一个共享变量,并都对其进行操作,就会导致得不到预期的结果。
  • 内存可见性:一个线程对一个共享变量的修改,另一个线程不一定能马上能看到,甚至永远也看不到。
    数据有可能被缓存在寄存器或CPU的缓存中,当去访问一个变量时可能直接在缓存中获取,而不一定会在内存中获取;同理,当修改一个变量时,可能先存入缓存,稍后才会同步到内存中。

Synchronized

  • 用法
    Synchronized用于保护对象。竞态条件和内存可见性问题均可用synchronized来解决。
  • 修饰实例方法
    public synchronized void test(){}
    Synchronized用于保护实例对象this。
    当使用Synchronized修饰方法,方法内的代码就变成了原子操作。即多个线程只能有一个调用同一对象的synchronized实例方法。
    当一个线程想要调用synchronized方法时,它首先会尝试获取锁,如果不能够获取锁进入等待队列,阻塞并等待唤醒,否则执行实例方法代码,万仇释放锁,并从等待队列中取一个线程唤醒,不保证公平性。
  • 静态方法
    public static synchronized void test(){}
    保护的是被修饰的静态方法的类对象。
  • 代码块
    synchronized(this){}
    synchronized括号里面的就是保护的对象。
  • 可重入性
    在对同一个执行线程,它在获得了锁之后再调用其他需要同样的锁的代码时可以直接调用,该特性是通过记录锁的持续线程和持有数量来进行实现。
  • 死锁
  • 原因:由于两个不同线程的分别等待对方所持有的锁。
  • 避免:避免在持有一个锁去申请另一个锁,如果确实需要多个锁,所有代码都应该按照相同的顺序去申请锁。或在获取不到锁时放弃已持有的锁再次尝试,防止死锁。

线程的优点及成本

  • 优点
    1. 充分利用多CPU的运算能力
    2. 充分利用硬件资源和网络。
    3. 在GUI保持程序的响应性。
    4. 简化建模和IO处理。
  • 成本
  • 创建线程时损耗的资源,必要的数据结构、栈、程序计数器
  • 线程切换时,需要保存当前线程的上下文到内存,并恢复切换的线程的上下文状态,此过程不仅耗时,且会使CPU的缓存失效。

协作机制

wait/notify两个方法是线程的协作的基本方法。只能在synchronized代码块中调用。

  • wait方法
    public final void wait() throws InterruptedException
    用于线程等待,可传入参数表示等待时间(毫秒),等待时间内可被中断,中断时抛出InterruptedException异常。
    每个对象都有一个等待队列,即条件队列,当调用此方法时,会将线程置于此队列中释放锁持有的锁并对线程进行阻塞,等待一个通过其他线程改变的条件。
    此时线程的状态变为:WAITING或TIMED_WAITING
  • notify方法
    public final native void notify();
    public final native void notifyAll();
    notify方法用于条件队列中随机选一个线程移除并唤醒,notifyAll方法用于唤醒条件队列中所有线程。
    如果被唤醒的线程能获得所需锁,则状态变为:RUNNABLE;否则状态变为BLOCKED。
  • InterruptedException异常
    改异常为受检异常,线程需要对其进行处理,处理方式一般有两种。
    1. 向上传递异常,由调用者进行处理
    2. 有些无法向上传递的情况,如run方法,需要捕获异常并进程清理操作,然后调用Thread的interrupt方法设置中断标志位。

线程的中断

  • 机制
    中断时一个线程的主要机制,不是指强制停止,而是一种协作机制,通过传递一个信号,由线程决定何时退出。
  • Thread中关于中断的方法
    1. public boolean isInterrupted():返回对应线程的中断标志位是否为true。
    2. public void interrupt():返回对应线程的中断标志位是否为true,且会清空中断标志位。
    3. public static boolean interrupted():中断对应线程,与线程的状态有关。
      • RUNNABLE:如没有执行I/O操作时,该方法会设置中断标志位为true,线程应在合适的位置检测中断标志位。
      • WAITING/TIMED_WAITING:该方法会使线程抛出InterruptedException,且会清除中断标志位。
      • NEW/TERMINATE:该方法在调用后无任何效果。
  • 正确的中断线程
    如果不明白线程在做什么,不应该贸然调用interrupted()方法,线程应当提供单独的取消/中断方法给调用者。
0 0
原创粉丝点击