zoukankan      html  css  js  c++  java
  • Java多线程之线程

    前言

    线程作为现代操作系统调度的最小单元,多个线程能够同时执行,这将显著提高程序的性能,而且在当前多核CPU的环境下也能更好的利用资源。Java提供了对多线程的良好支持。线程是多线程的基础。

    使用多线程的原因:1. 计算机的核心越来越多,更好的利用硬件资源。2. 更快的响应时间,对于复杂的业务逻辑能够多路进行,当然如果你只是打印一句"Hello World"确实"不需要多线程" 3. Java 已经为多线程编程提供了良好 的编程模型。开发人员可以直接使用,不必重复造轮子。

    进程和线程

    现代操作系统在允许一个程序时,会为其创建一个进程。而操作系统最小的调度单元是线程,一个进程中可以创建多个线程。

    这些线程拥有各自的计数器,堆栈和局部变量。这些线程能够访问进程中共享的内存变量。

    Daemon(守护线程) 线程是一种支持型的线程,它主要被作用于程序中后台调度以及支持性工作。用个比较通俗的比喻,任何一个守护线程都是整个JVM中所有非守护线程的保姆。守护线程最典型的应用就是 GC (垃圾回收器)

    只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

    创建线程的方式

    继承Thread 实现Runable

    • 继承Thread 重写run()方法
    • 实现Runnable接口重写run()方法

    阿里巴巴Java 开发规范中强制规定 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”(CPU上下文切换)的问题。

    线程 线程的状态

    • NEW 创建未运行中
    • RUNNING 调用start() 方法后运行中的状态
    • WAITING 等待状态,需notify() 方法通知后返回运行中状态
    • TIME WAITING 超时等待,在给定时间后返回运行中状态
    • BLOCKED 为获取到锁,进入到阻塞状态,在阻塞获取到锁后返回运行中状态
    • TERMINATED 终止状态

    线程的状态转换

    线程间的切换

    启动停止线程

    • start() 方法用于启动线程,run()方法是执行体,线程start()启动后会执行run() 方法体内的程序。

    停止线程

    • 中断
      • 它表示一个运行中的线程是否被其他线程进行中断操作(被其他线程停止)
      • 其他线程通过调用该线程的 interrupt() 方法对其进行中断操作
      • 线程通过isInterrupted()方法(返回中断标识符)来进行判断是否被中断,
    • 安全的终止线程
      • 类似于中断,通过一个变量的控制来停止线程
        private static class Runner implements Runnable {
        
            private volatile boolean on = true;
        
            @Override
            public void run() {
                while (on && !Thread.currentThread().isInterrupted()) {
                    // do something
                }
            }
        
            public void stopRunner() {
                on = false;
            }
        }
        

    sleep() 函数线程会让出CPU,给其他需要CPU资源的线程使用

    线程和线程之间的通信

    线程和线程之间是如何通信和进行协作的呢

    方式:

    • volatile 和 synchronized 关键字

      • 多个线程在自己内部都有共享变量的拷贝进行使用,修改共享变量,然后其他线程立刻感知到共享变量的修改
    • 等待通知机制

      • 等待通知机制是两个线程通过一个对象来进行交互,以这个对象作为媒介。一个线程A调用这个对象的wait() 方法进行等待状态,另一个线程B调用notifyAll进行通知。线程A接收到通知返回 进行后续操作。而且调用wait() 方法会释放这个对象的锁。
      • Object 类自带的等待/通知方法
        • notify() 通知一个在对象上等待的线程
        • notifyAll() 通知所有等待在该对象上的线程
        • wait() 调用该方法的线程进入WAITING状态,等待他人通知或中断才会返回
        • wait(long) 超时等待,可以选择等待的时间
        • wait(long,int) 超时等待,对于超时时间进行更细粒度的控制
      • 如何传参?通过共享变量
    • 管道输入/输出流

      • 管道输入/输出流用于线程之间的数据传输。包括了以下四种实现:PipedOutputStream、PipedInputStream、PipedReader、PipedWriter 前两种面向字节,后两种面向字符
      • 对于Poped类型的流,必须先进行绑定,也就是调用connect() 方法。
    • join()方法

      • 线程A 和线程thread ,当线程A执行了thread.join()语句。线程A等待thread线程终止后才从thread.join() 返回。
      • join(longmillis) join(long millis,int nanos)
    • ThreadLocal

      • 线程变量,每个线程可以绑定一个ThreadLocal。ThreadLocal是一个以ThreadLocal对象为键,任意对象为值的存储结构。
      • 对于同一个线程。set() 方法来设置一个值,get()方法获取原先设置的值。

    分享一道常见面试题三个线程交替打印从1-100

    // 使用 synchronized notifyAll() 实现 wait() 实现
    public class PrintNums {
    
        private Object lock = new Object();
        private volatile int curNum = 1;
        private volatile String curThread = "1";
    
        class Runner implements Runnable {
            @Override
            public void run() {
                while (curNum < 101) {
                    synchronized (lock) {
                        while (!Thread.currentThread().getName().equals(curThread)) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        if (curThread.equals("3")) {
                            curThread = "1";
                        } else {
                            curThread = String.valueOf(Integer.valueOf(curThread) + 1);
                        }
                        if (curNum < 101) {
                            System.out.println(
                                    "current thread: " + Thread.currentThread().getName()
                                            + " cur num: " + curNum++);
                        }
    
                        lock.notifyAll();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            PrintNums print = new PrintNums();
            Thread thread1 = new Thread(print.new Runner(), "1");
            Thread thread2 = new Thread(print.new Runner(), "2");
            Thread thread3 = new Thread(print.new Runner(), "3");
            
            thread1.start();
            thread2.start();
            thread3.start();
    
            System.out.println("main terminate");
        }
    
    }
    

    运行结果:

    main terminate
    current thread: 1 cur num: 1
    current thread: 2 cur num: 2
    current thread: 3 cur num: 3
    current thread: 1 cur num: 4
    current thread: 2 cur num: 5
    current thread: 3 cur num: 6
    

    References

    • 《Java并发编程的艺术》
  • 相关阅读:
    窗口生效函数UpdateData
    查找内容grep命令
    终止函数 atexit()
    根据名字杀死进程Killall
    修改系统时间为UTC时间
    转 proc文件
    NTP算法
    转载,网线的深刻理解
    js完成密码输入为空,和两次输入不一致
    CSS初步了解
  • 原文地址:https://www.cnblogs.com/wei57960/p/12901074.html
Copyright © 2011-2022 走看看