• Java基础~线程和进程(3) 多线程编程细节


    目录

    多线程编程细节1:Thread

    常见方法: 

     java针对线程提供了10级优先

     boolean isAlive()测试线程

     多线程编程细节2:Runnable和Callable

     Runnable接口

    Callable接口

     Future接口

     FutureTask具体使用

     多线程编程细节3:线程池

    Java内存模型


    多线程编程细节1:Thread

    Thread类实现了Runnable接口,所以Thread对象也是可运行Runnable对象,

    同时Thread类也是线程类

    Thread类定义

    Thread()//一般用于在Thread类中覆盖定义run方法,可以使用匿名内部类进行

    Thread(Runnable)//使用最多的情况,run方式是由Runnable参数对象提供

    Thread(String name) //自定义线程名称

    Thread(Runnable,String name)

    由于Runnable接口属于函数式接口,所以一般使用简化写法
    Thread t = new Thread(()->{
        System.out.println(Thread.currentThread());
    });
    t.start();

    常见方法: 

    常见方法
    void start()  使该线程开始执行,注意不是立即执行,不是一般方法调用;Java 虚拟机调用该线程的 run 方法
    void run()  线程的执行体 
    void setName(String) 改变线程名称
    void setPriority(int)    更改线程的优先级,Java中线程的优先级可以分为1-10,默认为5 
    void setDaemon(boolean)设置守护线程,守护线程是一种用于提供服务的线程,一般线程体中使用的是死循环,会在所有非守护线程退出后自动关闭
    void join()/(long millisec) 等待该线程终止的时间最长为 millis 毫秒 
    void interrupt()中断线程,不是中断线程的执行,而是修改中断参数 
    boolean isAlive() 测试线程是否处于活动状态,活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的
    static void yield() 暂停当前正在执行的线程对象,并执行其他线程
    static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响
    static Thread currentThread()  返回对当前正在执行的线程对象的引用 
    1. public class Test1 {
    2. public static void main(String[] args) {
    3. Thread t1=new Thread(()->{ //run方法不能直接调用,从语法角度上说是可以直接调用,但是直接调用则不是多线程
    4. for(int i=0;i<10;i++) {
    5. System.out.println(new Date().getTime());
    6. }
    7. });
    8. t1.start(); //启动线程并不意味着线程立即执行,具体线程什么时候开始运行取决于线程调度机制
    9. for(int i=0;i<10;i++) {
    10. System.out.println("Main:"+new Date().getTime());
    11. }
    12. }
    13. }

     java针对线程提供了10级优先

    优先级越高则获取更多的运行机会但是不同的操作系统不一定恰好支持10级优先,所以设置优先级时可能会出现在Java中的不同优先级映射到操作系统中相同的优先级上。在具体编程中如果需要使用优先级,则建议将优先级的差距拉开

    Thread类中定义了3个常量

    public static final int MIN_PRIORITY = 1;  最小优先级

    public static final int NORM_PRIORITY = 5;  默认优先级

    public static final int MAX_PRIORITY = 10;  最大优先级

    1. public class Test3 {
    2. public static void main(String[] args) {
    3. Thread t1 = new Thread(() -> {
    4. for (int i = 0; i < 1000; i++) {
    5. System.out.println(Thread.currentThread() + ":" + i);
    6. try {
    7. Thread.sleep(0);
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. }
    12. });
    13. t1.setPriority(Thread.MAX_PRIORITY);
    14. t1.start();
    15. for (int i = 0; i < 1000; i++) {
    16. System.out.println("Main:" + i);
    17. try {
    18. Thread.sleep(0);
    19. } catch (InterruptedException e) {
    20. e.printStackTrace();
    21. }
    22. }
    23. }
    24. }

     一般守护线程都会使用死循环,因为自动终止,设置守护线程应该在start方法之前

     void interrupt()中断线程,不是中断线程的执行,而是修改中断参数具体实现是依赖于调用interrupt方法后产生InterruptedException实现的

     

    1. public class Test6 {
    2. public static void main(String[] args) {
    3. Thread t1 = new Thread(() -> {
    4. while (true) {
    5. System.out.println(Thread.currentThread());
    6. try {
    7. Thread.sleep(1);
    8. } catch (InterruptedException e) {
    9. break;
    10. }
    11. }
    12. });
    13. t1.start();
    14. t1.interrupt();
    15. System.out.println("Main end....");
    16. }
    17. }

     boolean isAlive()测试线程

    是否处于活动状态,活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的

    1. public class Test7 {
    2. public static void main(String[] args) {
    3. Thread t1 = new Thread(() -> {
    4. System.out.println(Thread.currentThread());//Thread[Thread-0,5,main]
    5. });
    6. t1.start();
    7. try {
    8. Thread.sleep(10);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. System.out.println(t1.isAlive());
    13. System.out.println(t1);//Thread[Thread-0,5,]
    14. }
    15. }

     多线程编程细节2:Runnable和Callable

     Runnable接口

    Runnable接口只定义了一个方法public void run(),这个方法要求实现

    Runnable接口的类实现,Runnable对象称为可运行对象,一个线程的运行

    就是执行该对象的run()方法

    run()方法没有返回值void,而且不能抛出异常

    Thread类中的定义

     private Runnable target;
      @Override
        public void run() {
            if (target != null) {
                target.run();
            }
        }

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. // 简化写法
    4. new Thread(() -> {
    5. for (int i = 0; i < 10; i++) {
    6. System.out.println("左手画一条龙...");
    7. try {
    8. Thread.sleep(30);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. }).start();
    14. }
    15. }
    16. class MyRunnable implements Runnable {
    17. @Override
    18. public void run() throws Exception {//语法报错,这里不允许抛出异常,如果其中有异常则需要使用try.catch处理
    19. // 没有返回值,如果需要记录处理结果,需要自己编程处理
    20. }
    21. }

    Callable接口

    继承Thread或实现Runnable接口这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。 

    如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。

    call()方法有返回值,这个返回值可以通过泛型进行约束,允许抛出异常

    1. public class Test2 {
    2. public static void main(String[] args) {
    3. // 简化写法
    4. new Thread(new FutureTask<>(() -> {
    5. for (int i = 0; i < 10; i++) {
    6. System.out.println("右手画彩虹");
    7. Thread.sleep(30);// 因为call方法允许抛出异常
    8. }
    9. return null;
    10. })).start();
    11. }
    12. }
    13. class MyRunnable2 implements Callable {
    14. // <>中用于指定返回值类型,必须使用引用类型,不能使用简单类型
    15. public Number call() throws Exception {// 允许抛出异常
    16. return null;
    17. }
    18. }

     Future接口

    Future表示一个任务的生命周期,并提供了方法来判断是否已经完成或取消以及获取任务的结果和取消任务等
    Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、
    获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
    cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回
    false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务
    如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论
    mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true
    isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取
    消成功,则返回 true
    isDone方法表示任务是否已经完成,若任务完成,则返回true
    有用的方法

    get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕
    才返回
    get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就抛出TimeoutException超时异常。 

     Future接口的具体实现类
      public class FutureTask implements RunnableFuture
      
     public interface RunnableFuture extends Runnable, Future {
        void run();
    }
      public FutureTask(Callable callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
        }

    1. public class Test3 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Future f = new FutureTask(() -> {
    4. for (int i = 0; i < 1000; i++) {
    5. System.out.println(Thread.currentThread() + "...start..." + i);
    6. Thread.sleep(10); //模拟一个处理过程
    7. System.out.println(Thread.currentThread() + "...end..." + i);
    8. }
    9. return null;
    10. });
    11. if (f instanceof Runnable) {
    12. new Thread((Runnable) f).start();
    13. int counter = 0;
    14. while (true) {
    15. Thread.sleep(20);
    16. System.out.println("任务是否被取消:" + f.isCancelled() + "--" + counter);
    17. System.out.println("任务是否执行完毕:" + f.isDone());
    18. counter++;
    19. if (counter > 10)
    20. f.cancel(true);// 取消任务的执行
    21. if (counter > 50)
    22. break;
    23. }
    24. }
    25. }
    26. }

     FutureTask具体使用

    1. FutureTask future = new FutureTask(callable);
    2. new Thread(future).start();

    FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,
    又可以作为Future得到Callable的返回值

    FutureTask是一个可取消的异步计算,FutureTask实现了Future的基本方法,提供start 
    cancel 操作,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算
    完成之后获取,get方法会阻塞当计算没有完成的时候,一旦计算已经完成, 那么计算就
    不能再次启动或是取消。

    一个FutureTask 可以用来包装一个 Callable 或是一个Runnable对象。因为FurtureTask
    实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution). 
    它同时实现了Callable, 所以也可以作为Future得到Callable的返回值。

    1. package com.yan2;
    2. import java.util.concurrent.ExecutionException;
    3. import java.util.concurrent.Future;
    4. import java.util.concurrent.FutureTask;
    5. import java.util.concurrent.TimeUnit;
    6. import java.util.concurrent.TimeoutException;
    7. public class Test31 {
    8. public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
    9. Future f = new FutureTask(() -> {
    10. int res = 0;
    11. for (int i = 0; i < 1000; i++) {
    12. Thread.sleep(10);
    13. res += i;
    14. }
    15. return res;
    16. });
    17. if (f instanceof Runnable) {
    18. new Thread((Runnable) f).start();
    19. int counter = 0;
    20. long start = System.currentTimeMillis();
    21. // Object obj=f.get();
    22. Object obj = f.get(5, TimeUnit.SECONDS);// 参数1为超时时长,参数2为时长的单位,是一个枚举类型数据,超时TimeoutException
    23. long end = System.currentTimeMillis();
    24. System.out.println("get...执行时间为:" + (end - start) + "ms");
    25. System.out.println("线程执行结果为:" + obj);
    26. }
    27. }
    28. }

     多线程编程细节3:线程池

    ThreadPoolExecutor是线程池框架的一个核心类,线程池通过线程复用机制,并对线程进行统一管理 
    降低系统资源消耗。通过复用已存在的线程,降低线程创建和销毁造成的消耗;
    提高响应速度。当有任务到达时,无需等待新线程的创建便能立即执行;
    提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源
    还会降低系统的稳定性,使用线程池可以进行对线程进行统一的分配、调优和监控。 
    线程池的运行状态总共有5种,其值和含义分别如下:
    RUNNING: 高3位为111,接受新任务并处理阻塞队列中的任务
    SHUTDOWN: 高3位为000,不接受新任务但会处理阻塞队列中的任务
    STOP: 高3位为001,不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在
    运行的任务
    TIDYING: 高3位为010,所有任务都已终止,工作线程数量为0,线程池将转化到
    TIDYING状态,即将要执行terminated()结束钩子方法
    TERMINATED: 高3位为011,terminated()方法已经执行结束

     runState is stored in the high-order bits
        private static final int RUNNING    = -1 << COUNT_BITS;
        private static final int SHUTDOWN   =  0 << COUNT_BITS;
        private static final int STOP       =  1 << COUNT_BITS;
        private static final int TIDYING    =  2 << COUNT_BITS;
        private static final int TERMINATED =  3 << COUNT_BITS;

    Java内存模型


    Java内存模型定义了一种多线程访问Java内存的规范。

    Java内存模型将内存分为了主内存和工作内存。类的状态也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,会将最新的值更新到主内存中去
    定义了几个原子操作,用于操作主内存和工作内存中的变量
    定义了volatile变量的使用规则
    happens-before即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,

     

  • 相关阅读:
    准备好迁移上云了?请收下这份迁移步骤清单
    socket结合线程的测试demo
    FreeRTOS学习笔记(一)
    简单的kafka&redis学习之redis
    ECharts快速入门
    css实现盒子的四周阴影
    计算机毕业设计Java妇女健康保健系统(源码+系统+mysql数据库+lw文档)
    触发器(1)
    Swift基础语法 - 可选项
    A-Level Economics 真题及答案解析
  • 原文地址:https://blog.csdn.net/qq_51222096/article/details/126413778