• Java


    多线程

    多线程创建的三种方式:

    1. 继承 Thread 类,重写 run 方法。
    2. 实现 Runnable 接口。
    3. 实现 Callable 接口。这个方法比较编写代码比较复杂,是 JDK 1.5 出来的。

    方式 1 和 方式 2 的区别:

    1. extends 是继承,一般要增强类的功能使用,重写或添加方法。
    2. implements 是实现接口。
    3. 单继承和多实现。

    Thread 类源码分析

    从官方文档得到:

    • JVM 是支持多线程的。
    • 线程是有优先级的。当在线程中创建一个线程对象时,新对象的优先级和当前线程相同,当且仅当线程是守护线程,新的线程对象才能是守护线程。
    • 创建线程的方式:继承 Thread 类;实现 Runnable 接口。
    //继承 Thread 类
    class PrimeThread extends Thread {
        long minPrime;
        PrimeThread(long minPrime) {
            this.minPrime = minPrime;
        }
    
        public void run() {
            // compute primes larger than minPrime
            . . .
        }
    }
    
    //给出调用方法
    PrimeThread p = new PrimeThread(143);
    p.start();
    
    //实现 Runnable 接口
    class PrimeRun implements Runnable {
        long minPrime;
        PrimeRun(long minPrime) {
            this.minPrime = minPrime;
        }
    
        public void run() {
            // compute primes larger than minPrime
            . . .
        }
    }
    
    //给出调用方法
    PrimeRun p = new PrimeRun(143);
    new Thread(p).start();
    

    从源码级别可以看到这两种创建线程方法的区别,一个是直接创建继承 Thread 类的子类对象,子类对象可以直接调用 start 方法;另一个是使用了 Thread 类不同的构造器,传入的对象是一个实现了 Runnable 接口的类对象。

    Thread.State - Thread 的内部枚举类

    Thread 类从源码可以看到,线程一共有六种状态,分别是:

    public enum State {
        //A thread that has not yet started is in this state. 
        //新建,刚刚 new 出来,还没有调用 start 方法
        NEW,
        
        //A thread executing in the Java virtual machine is in this state. 
        //线程在虚拟机中正在运行
        RUNNABLE,
        
        //A thread that is blocked waiting for a monitor lock is in this state.
        //被锁住了
        BLOCKED,
        
        //A thread that is waiting indefinitely for another thread to perform a particular action is in this state. 
        //处于等待状态
        WAITING,
        
        //A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
        //确定时间的等待状态
        TIMED_WAITING,
        
        //A thread that has exited is in this state. 
    	//线程已经退出,即终结了
        TERMINATED;
    }
    

    感兴趣的同学可以再去看看源码。

    synchronized

    synchronized 简单解读

    原理可以看这篇博客 https://www.cnblogs.com/paddix/p/5367116.html ,Object 类有一个隐式的 monitor,在调用 synchronized 修饰的方法时,会调用 monitorenter 指令,此时犹如房门上锁,房内存放的都是 synchronized 方法,这种加锁的方式很粗暴,业内称重量级锁。这种说法很容易理解,只需要选择一个方法使用却将很多同步方法一起锁住,妨碍了其他方法的调用,在并发编程中很少使用这种加锁方式。

    Object 类的 notify notifyAll 和 wait 方法也需要 monitor 的作用,因此在使用这三个方法需要在 synchronized 修饰的方法或代码块下,由于三个方法都是 Object 类的 final 方法,意味着 Java 世界所有的对象都有这三个方法。

    测试:不同线程调用同一对象的同步方法和非同步方法

    public class SynchronizedTest01 {
    
        public static void main(String[] args) {
            Print print = new Print();
            new Thread(() -> {
                print.printA();
            }, "A").start();
    
            new Thread(() -> {
                print.printB();
            }, "B").start();
    
    
            try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); }
    
            new Thread(() -> {
                print.printA();
            }, "A").start();
    
            new Thread(() -> {
                print.printX();
            }, "X").start();
        }
    }
    
    class Print {
        synchronized void printA() {
            System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我已经调用了 printA() ,但是我要休息 3s");
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("A");
        }
    
        synchronized void printB() {
            System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我等了 A 线程 3s ,这货终于好了,我赶紧打印");
            System.out.println("B");
        }
    
        void printX() {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我看 A 线程要休息 3s ,我休息 1s 后赶紧打印");
            System.out.println("X");
        }
    }
    

    输出结果:对照 code 和输出的内容可以很好的理解。

    我是 A 线程,我已经调用了 printA() ,但是我要休息 3s
    A
    我是 B 线程,我等了 A 线程 3s ,这货终于好了,我赶紧打印
    B
    我是 A 线程,我已经调用了 printA() ,但是我要休息 3s
    我是 X 线程,我看 A 线程要休息 3s ,我休息 1s 后赶紧打印
    X
    A
    

    结论:同一个对象,其同步方法不能同时调用,非同步方法不受此限制。

  • 相关阅读:
    容器编排系统k8s之ReplicaSet和Deployment控制器
    容器编排系统k8s之Pod生命周期、健康/就绪状态探测以及资源限制
    容器编排系统k8s之资源标签、标签选择器、资源注解
    容器编排系统k8s之Pod资源配置清单基础
    容器编排系统k8s之Kubectl工具的基础使用
    容器编排系统k8s之基础入门
    抢先看:笔者亲历的2020年中国.NET开发者大会活动纪实
    谷歌的请求索引功能恢复了
    VuePress教程之深入理解插件API
    Linux将shell脚本配置成系统服务并设置开机自启
  • 原文地址:https://www.cnblogs.com/chenxianbin/p/12132104.html
Copyright © 2020-2023  润新知