多线程理论基础

简介: 多线程理论基础

 

2. Java线程详解

2.1 Java线程的实现方式

  1. 通过继承Thread类实现(Thread类实现了Runnable接口)
  2. 实现Runnable接口(Runnable接口里就一个run方法)。
  3. 实现Callable接口(Callable接口里就一个call方法)。

      1>、用FutureTask执行Callable接口的实现类,实现精准接收返回值。

 2>、 用线程池的submit方法执行Callable接口的实现类,可以实现线程池并发的处理结果,方便对Callable实现类的执行方式做统一的管理。

     4. 使用lambda表达式

new Thread(() ‐ > System.out.println(Thread.currentThread().getName())).start();

2.2 run方法和start方法的区别

    Run方式是Runnale接口中定义的抽象方法经实现类实现的。主要用来处理要实现的逻辑,可以说就是一个普通方法。而Start方法是Thread类的中的方法,它里面调用了一个叫start0 的native方法。start0里面调用jvm的startThread的方法来创建一个子线程,并调用线程的run方法开始线程逻辑的处理。

2.3 Java线程的调度机制

在计算机中,线程调度有两种模型,分别是分时调度模型和抢占式调度模型。

分时调度模型:指让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片。

抢占式调度模型:让可运行迟中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个线程使其占用CPU,当它失去了CPU的使用权后,再随机选择其它线程获取CPU的使用权。

 java虚拟机默认采用抢占式调度模型,但在某些特定的需求下需要改变这种模型,由线程自己来控制CPU的调度。

2.4 多线程的六种状态

1. 新建状态(New)

2. 运行状态(Runnable):包含Running和Ready。调用start方法后进入的状态。

3. 无限期等待(Waiting):不会被分配cpu执行时间,需要显式唤醒。

  进入此状态方式:

  • 无参的Object.wait();
  • 无参的Thread.join();

4.  限期等待(Timed  Waiting):在一定时间后系统自动唤醒。

  • 入参的Object.wait();
  • 入参的Thread.join();
  • 入参的Thrad.sleep();

5. 阻塞状态(Blocked):等待获取排它锁。

6. 结束状态(Terminated   /'tɝmə,net/):线程执行完毕。

2.5 Thread常用方法

  • sleep和wait的区别
  1. sleep是Thread类中的方法,而wait是Object的方法
  2. sleep可以在任何地方使用,而wait只能在synchronized方法或synchronized代码块中使用(lock.wait())。
  3. sleep的主要作用是使当前线程让出cpu资源,不会导致锁行为的改变;而wait不仅会让出cpu,而且还会释放已占有的同步锁资源。线程调用的wait方法后就进入了线程等待池中,直到线程完成了指定的等待时间或者锁对象调用了notify/notifyall方法后,线程才重新回到锁池中等待锁资源的获取。
  • notify和notifyall的区别

    Notify只会随机让一个处于等待池中的线程进入锁池中去竞争锁的机会,而notifyall会让所有处在等待池中的线程进入锁池去竞争锁资源,没有获取锁资源的线程也只能继续待在锁池中等待其他机会获取锁资源,而不能回到等待池中。

  • yield方法

      当调用Thread.yield函数时,会给线程调度器一个当前线程愿意让出cpu使用的暗示,但线程调度器可能会忽略这个暗示。yield()不会释放当前线程占用的锁资源。线程调用yield方法后,只有与当前线程优先级相同或者更高的线程才能获得执行的机会。

  • join方法

     join方法用于阻塞当前线程,等到调用join方法的线程执行完毕后再来执行当前线程。一般用于等待异步线程执行完结果之后才能继续运行的场景。

Thread t = new Thread(new Runnable() { 
  @Override 
  public void run() { 
    System.out.println("t begin"); 
    try { 
      Thread.sleep(5000); 
    } catch (InterruptedException e) { 
      e.printStackTrace(); 
    } 
    System.out.println("t finished"); 
  } 
}); 
long start = System.currentTimeMillis(); 
t.start(); 
//主线程等待线程t执行完成 
t.join();
System.out.println("执行时间:" + (System.currentTimeMillis() ‐ start));

2.6 Thread中断方法

  • stop方法

   Thread类中方法,暴力中断线程,不论线程此时处于什么状态。

  • interrupt实例方法
  1. 1.当线程状态在正常运行状态时,改变线程的中断标识为true(即isInterrupted()返回值为true),但不会对线程的运行产生任何影响。
  2. 2.当线程为阻塞状态时,抛出InterruptedException异常,将线程从阻塞状态中唤醒,线程恢复运行状态。不会改变线程的中断标识,
  • interrupted静态方法

     Interrupted是静态方法,它里面调用了currentThread.isInterrupted(true),它会返回当前线程的中断标识,并重置为false。

  • isInterrupted实例方法

      判断当前线程的中断标识,如果是true则返回true,是Flase则返回flase,不会线程的中断标识做处理。

while (!Thread.currentThread().isInterrupted() && more work to do) { 
 do more work 
}

2.7 线程通信方法

  • volatile

    volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。一个变量被volatile修饰时,线程每次读取变量必须从主存中获取,每次修改变量也会立刻刷到主存。它时通过内存屏障实现了变量可见性和禁止指令重排序。

  • 等待唤醒(等待通知)机制

      等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法, 线程将进入等待队列进行等待直到被唤醒。

public class WaitDemo {
    private static Object lock = new Object();
    private static boolean flag = true;
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    while (flag) {
                        try {
                            System.out.println("wait start .......");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("wait end ....... ");
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (flag) {
                    synchronized (lock) {
                        if (flag) {
                            lock.notify();
                            System.out.println("notify .......");
                            flag = false;
                        }
                    }
                }
            }
        }).start();
    }
}
  • LockSupport()

   LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用 unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。

public class LockSupportTest {
    public static void main(String[] args) {
        Thread parkThread = new Thread(new ParkThread());
        parkThread.start();
        System.out.println("唤醒parkThread");
        LockSupport.unpark(parkThread);
    }
    static class ParkThread implements Runnable {
        @Override
        public void run() {
            System.out.println("ParkThread开始执行");
            LockSupport.park();
            System.out.println("ParkThread执行完成");
        }
    }
}
  • 管道输入输出流

 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程 之间的数据传输,而传输的媒介为内存。管道输入/输出流主要包括了如下4种具体实现: PipedOutputStream、PipedInputStream、PipedReader和PipedWriter,前两种面向字节,而后两种面向字符

public class Piped {
    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // 将输出流和输入流进行连接,否则在使用时会抛出IOException
        out.connect(in);
        Thread printThread = new Thread(new Print(in), "PrintThread");
        printThread.start();
        int receive = 0;
        try {
            while ((receive = System.in.read()) != ‐1){
                out.write(receive);
            }
        } finally {
            out.close();
        }
    }
    static class Print implements Runnable {
        private PipedReader in;
        public Print(PipedReader in) {
            this.in = in;
        }
        @Override
        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != ‐1){
                    System.out.print((char) receive);
                }
            } catch (IOException ex) {
            }
        }
    }
}
  • Thread.join

      join可以理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的。

2.8 进程通信的方式:

  1. linux管道
  2. 通过linux的mkfifo命令来创建管道来实现两个进程的通讯,这种方式频繁交互数据。
  3. 消息队列
  4. 共享内存
  5. 比如一个微服务生成一个文件,让nginx挂到这个目录下读取。
  6. 信号量
  7. 比如 redis的分布式锁,多个进程利用这个分布式锁的有无信号可以实现通讯。
  8. socket通信
  9. 比如http、dubbo。
相关文章
|
2月前
|
存储 监控 算法
深入探究Java线程池:提升并发性能的利器
在当今高度并发的应用开发中,Java线程池作为一种广泛应用的并发编程技术,提供了一种优雅且高效的线程管理方案。本文深入探究Java线程池的相关技术,涵盖其核心概念、优势、常见类型(如FixedThreadPool、CachedThreadPool、SingleThreadExecutor、ScheduledThreadPool、ForkJoinPool及WorkStealingPool)、核心参数配置、异常处理与监控方法,以及性能调优的最佳实践,帮助读者更好地理解和应用线程池,从而提升并发性能。
|
3月前
|
安全 Java API
揭秘Java并发编程的神秘面纱:线程安全与性能优化之间的微妙舞蹈,如何让你的程序在多核时代中翱翔!
【8月更文挑战第12天】随着多核处理器的普及,Java并发编程越发重要。线程安全确保多线程环境下的程序一致性,而性能优化则让程序高效运行。通过同步机制如`synchronized`关键字或`ReentrantLock`接口,我们可以实现线程安全,如在银行账户存款操作中限制并发访问。然而,过度同步会导致性能下降,因此采用细粒度锁和利用Java并发工具类(如`ConcurrentHashMap`)可提高程序的并发能力。理解这些概念并加以实践,是每个Java开发者提升技能的关键。
48 0
|
6月前
|
存储 缓存 Java
【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你认识和了解JMM并发模型的基本原理
每位Java开发者都了解到Java字节码是在Java运行时环境(JRE)上执行的。JRE包含了最为关键的组成部分:Java虚拟机(JVM),它负责分析和执行Java字节码。通常情况下,大多数Java开发者无需深入了解虚拟机的内部运行原理。即使对虚拟机的运行机制不甚了解,也不会对开发工作产生太多影响。然而,对JVM有一定了解的话,将更有助于深入理解Java语言,并解决一些看似困难的问题。
85 4
【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你认识和了解JMM并发模型的基本原理
|
6月前
|
分布式计算 安全 Java
线程状态:深入理解多任务并发编程中的精髓
线程状态:深入理解多任务并发编程中的精髓
|
6月前
|
存储 算法 Java
理解进程调度的基本过程,为学好多线程打好基础
理解进程调度的基本过程,为学好多线程打好基础
56 0
理解进程调度的基本过程,为学好多线程打好基础
|
存储 缓存
并发编程(一)计算机理论模型
并发编程(一)计算机理论模型
74 0
|
存储 SQL 安全
搞定|通过实际案例搞懂多任务线程
搞定|通过实际案例搞懂多任务线程
搞定|通过实际案例搞懂多任务线程
|
存储 缓存 安全
Java并发编程系列之一并发理论基础
本系列文章开始Java并发编程的进阶篇的学习,为了初学者对多线程的初步使用有基本概念和掌握,前置知识会对一些基础篇的内容进行介绍,以使初学者能够丝滑入戏。
Java并发编程系列之一并发理论基础
|
Java
【多线程3:基础原理】
【多线程3:基础原理】
112 0
|
算法 安全 Java
『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事
『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事
『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事