Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(一)

简介: Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(一)

Java中的线程

Java之父对线程的定义是:

线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进程所创建的对象资源(内存资源)。java.lang.Thread对象负责统计和控制这种行为。

每个程序都至少拥有一个线程-即作为Java虚拟机(JVM)启动参数运行在主类main方法的线程。在Java虚拟机初始化过程中也可能启动其他的后台线程。这种线程的数目和种类因JVM的实现而异。然而所有用户级线程都是显式被构造并在主线程或者是其他用户线程中被启动。

      本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。在这之前,首先让我们来了解下在操作系统中进程和线程的区别:复制代码
      进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)复制代码
      线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)复制代码
      线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。复制代码
      多进程是指操作系统能同时运行多个任务(程序)。复制代码
      多线程是指在同一程序中有多个顺序流在执行。复制代码
    在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用复制代码

Java线程状态机

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

  • 新建状态:
    使用 **new** 关键字和 **Thread** 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 **start()** 这个线程。复制代码
  • 就绪状态:
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。复制代码
  • 运行状态:
    如果就绪状态的线程获取 CPU 资源,就可以执行 **run()**,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。复制代码
  • 阻塞状态:
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:复制代码
    *   等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。复制代码
    *   同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。复制代码
    *   其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。复制代码
  • 死亡状态:
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
## Java多线程实战
## 多线程的实现复制代码
    public class 多线程实例 {复制代码
        //继承thread
        @Test
        public void test1() {
            class A extends Thread {
                @Override
                public void run() {
                    System.out.println("A run");
                }
            }
            A a = new A();
            a.start();
        }复制代码
        //实现Runnable
        @Test
        public void test2() {
            class B implements Runnable {复制代码
                @Override
                public void run() {
                    System.out.println("B run");
                }
            }
            B b = new B();
            //Runable实现类需要由Thread类包装后才能执行
            new Thread(b).start();
        }复制代码
        //有返回值的线程
        @Test
        public void test3() {
            Callable callable = new Callable() {
                int sum = 0;
                @Override
                public Object call() throws Exception {
                    for (int i = 0;i < 5;i ++) {
                        sum += i;
                    }
                    return sum;
                }
            };
            //这里要用FutureTask,否则不能加入Thread构造方法
            FutureTask futureTask = new FutureTask(callable);
            new Thread(futureTask).start();
            try {
                System.out.println(futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }复制代码
        //线程池实现
        @Test
        public void test4() {
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            //execute直接执行线程
            executorService.execute(new Thread());
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("runnable");
                }
            });
            //submit提交有返回结果的任务,运行完后返回结果。
            Future future = executorService.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return "a";
                }
            });
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }复制代码
            ArrayList<String> list = new ArrayList<>();
            //有返回值的线程组将返回值存进集合
            for (int i = 0;i < 5;i ++ ) {
                int finalI = i;
                Future future1 = executorService.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        return "res" + finalI;
                    }
                });
                try {
                    list.add((String) future1.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
            for (String s : list) {
                System.out.println(s);
            }
        }
    }复制代码

线程状态转换

    public class 线程的状态转换 {
    //一开始线程是init状态,结束时是terminated状态
    class t implements Runnable {
        private String name;
        public t(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println(name + "run");
        }
    }复制代码
    //测试join,父线程在子线程运行时进入waiting状态
    @Test
    public void test1() throws InterruptedException {
        Thread dad = new Thread(new Runnable() {
            Thread son = new Thread(new t("son"));
            @Override
            public void run() {
                System.out.println("dad init");
                son.start();
                try {
                    //保证子线程运行完再运行父线程
                    son.join();
                    System.out.println("dad run");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //调用start,线程进入runnable状态,等待系统调度
        dad.start();
        //在父线程中对子线程实例使用join,保证子线程在父线程之前执行完复制代码
    }复制代码
    //测试sleep
    @Test
    public void test2(){
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 run");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });复制代码
        //主线程休眠。进入time waiting状态
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();复制代码
    }复制代码
    //线程2进入blocked状态。
    public static void main(String[] args) {
        test4();
        Thread.yield();//进入runnable状态
    }复制代码
    //测试blocked状态
    public static void test4() {
        class A {
            //线程1获得实例锁以后线程2无法获得实例锁,所以进入blocked状态
            synchronized void run() {
                while (true) {
                    System.out.println("run");
                }
            }
        }
        A a = new A();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 get lock");
                a.run();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t2 get lock");
                a.run();
            }
        }).start();复制代码
    }复制代码
    //volatile保证线程可见性
    volatile static int flag = 1;
    //object作为锁对象,用于线程使用wait和notify方法
    volatile static Object o = new Object();
    //测试wait和notify
    //wait后进入waiting状态,被notify进入blocked(阻塞等待锁释放)或者runnable状态(获取到锁)
    public void test5() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //wait和notify只能在同步代码块内使用
                synchronized (o) {
                    while (true) {
                        if (flag == 0) {
                            try {
                                Thread.sleep(2000);
                                System.out.println("thread1 wait");
                                //释放锁,线程挂起进入object的等待队列,后续代码运行
                                o.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("thread1 run");
                        System.out.println("notify t2");
                        flag = 0;
                        //通知等待队列的一个线程获取锁
                        o.notify();
                    }
                }
            }
        }).start();
        //解释同上
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (o) {
                        if (flag == 1) {
                            try {
                                Thread.sleep(2000);
                                System.out.println("thread2 wait");
                                o.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("thread2 run");
                        System.out.println("notify t1");
                        flag = 1;
                        o.notify();
                    }
                }
            }
        }).start();
    }复制代码
    //输出结果是
    //    thread1 run
    //    notify t2
    //    thread1 wait
    //    thread2 run
    //    notify t1
    //    thread2 wait
    //    thread1 run
    //    notify t2
    //不断循环复制代码
    }
## Java Thread常用方法复制代码

yield():

执行此方法会向系统线程调度器(Schelduler)发出一个暗示,告诉其当前JAVA线程打算放弃对CPU的使用,但该暗示,有可能被调度器忽略。使用该方法,可以防止线程对CPU的过度使用,提高系统性能。

Thread#sleep(time)或Thread.sleep(time, nanos):

使当前线程进入休眠阶段,状态变为:TIME_WAITING

Thread.interrupt():

中断当前线程的执行,允许当前线程对自身进行中断,否则将会校验调用方线程是否有对该线程的权限。

如果当前线程因被调用Object#wait(),Object#wait(long, int), 或者线程本身的join(), join(long),sleep()处于阻塞状态中,此时调用interrupt方法会使抛出InterruptedException,而且线程的阻塞状态将会被清除。

interrupted(),返回true或者false:

查看当前线程是否处于中断状态,这个方法比较特殊之处在于,如果调用成功,会将当前线程的interrupt status清除。所以如果连续2次调用该方法,第二次将返回false。

Thread.isInterrupted(),返回true或者false:

与上面方法相同的地方在于,该方法返回当前线程的中断状态。不同的地方在于,它不会清除当前线程的interrupt status状态。

join(time):

A线程调用B线程的join()方法,将会使A等待B执行,直到B线程终止。如果传入time参数,将会使A等待B执行time的时间,如果time时间到达,将会切换进A线程,继续执行A线程。


Java基础18-一文搞懂Java多线程使用方式、实现原理以及常见面试题(二):https://developer.aliyun.com/article/1535712

目录
相关文章
|
3天前
|
安全 算法 Java
Java 中的并发控制:锁与线程安全
在 Java 的并发编程领域,理解并正确使用锁机制是实现线程安全的关键。本文深入探讨了 Java 中各种锁的概念、用途以及它们如何帮助开发者管理并发状态。从内置的同步关键字到显式的 Lock 接口,再到原子变量和并发集合,本文旨在为读者提供一个全面的锁和线程安全的知识框架。通过具体示例和最佳实践,我们展示了如何在多线程环境中保持数据的一致性和完整性,同时避免常见的并发问题,如死锁和竞态条件。无论你是 Java 并发编程的新手还是有经验的开发者,这篇文章都将帮助你更好地理解和应用 Java 的并发控制机制。
|
8天前
|
安全 Java 开发者
Java并发编程中的线程安全性与性能优化
在Java编程中,处理并发问题是至关重要的。本文探讨了Java中线程安全性的概念及其在性能优化中的重要性。通过深入分析多线程环境下的共享资源访问问题,结合常见的并发控制手段和性能优化技巧,帮助开发者更好地理解和应对Java程序中的并发挑战。 【7月更文挑战第14天】
|
8天前
|
监控 Java API
Java并发编程之线程池深度解析
【7月更文挑战第14天】在Java并发编程领域,线程池是提升性能、管理资源的关键工具。本文将深入探讨线程池的核心概念、内部工作原理以及如何有效使用线程池来处理并发任务,旨在为读者提供一套完整的线程池使用和优化策略。
|
9天前
|
NoSQL Java 应用服务中间件
Java高级面试题
Java高级面试题
|
9天前
|
网络协议 安全 前端开发
java面试题
java面试题
|
9天前
|
NoSQL Java 关系型数据库
常见Java面试题
常见Java面试题
|
16天前
|
存储 算法 Java
Java面试之SpringCloud篇
Java面试之SpringCloud篇
33 1
|
16天前
|
缓存 NoSQL Redis
Java面试之redis篇
Java面试之redis篇
36 0
|
16天前
|
SQL 关系型数据库 MySQL
java面试之MySQL数据库篇
java面试之MySQL数据库篇
23 0
java面试之MySQL数据库篇
|
16天前
|
存储 缓存 前端开发
Java八股文面试之多线程篇
Java八股文面试之多线程篇
24 0
Java八股文面试之多线程篇