java并发编程 | 线程详解

简介: java并发编程 | 线程详解进程与线程进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配的最小单位,一个进程包含多个线程线程:线程是cpu调度的最小单位,每个线程拥有各自的计数器,对战和局部变量等属性,并且能过访问共享的内存变量线程的状态java线...

java并发编程 | 线程详解
进程与线程
进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配的最小单位,一个进程包含多个线程

线程:线程是cpu调度的最小单位,每个线程拥有各自的计数器,对战和局部变量等属性,并且能过访问共享的内存变量

线程的状态
java线程的生命周期总共包括6个阶段:

初始状态:线程被创建,但是还没有调用start()方法
运行状态:java中将就绪状态和运行状态统称为运行状态
阻塞状态:线程阻塞,线程等待进入synchronized修饰的代码块或方法
等待状态:线程进入等待状态,需要调用notify()或notifyAll()进行唤醒
超时等待状态:线程进入等待状态,在指定时间后自行返回
终止状态:线程执行完毕
在某一时刻,线程只能处于其中的一个状态

线程初始化后,调用start()方法变为运行状态,调用wait(),join()等方法,线程由运行状态变为等待状态,调用notify()或notifyAll()等方法,线程由等待状态变成运行状态,超时等待状态就是在等待状态基础上加了时间限制,超过规定时间,自动更改为运行状态,当需要执行同步方法时,如果没有获得锁,这时线程状态就变为阻塞状态,直到获取到锁,变为运行状态,当执行完线程的run()方法后,线程变为终止状态

创建线程
创建线程有三种方式

继承Thread类
实现Runnable接口
实现Callable接口
继承Thread类

/**

  • @author: chenmingyu
  • @date: 2019/4/8 15:13
  • @description: 继承Thread类
    */

public class ThreadTest extends Thread{

@Override
public void run() {
    IntStream.range(0,10).forEach(i->{
        System.out.println(this.getName()+":"+i);
    });
}

public static void main(String[] args) {
    Thread thread = new ThreadTest();
    thread.start();
}

}
实现Runnable接口
/**

  • @author: chenmingyu
  • @date: 2019/4/8 15:18
  • @description: 实现Runnable接口
    */

public class RunnableTest implements Runnable {

@Override
public void run() {
    IntStream.range(0,10).forEach(i->{
        System.out.println(Thread.currentThread().getName()+":"+i);
    });
}

public static void main(String[] args) {
    Runnable runnable = new RunnableTest();
    new Thread(runnable,"RunnableTest").start();
}

}
实现Callable接口
/**

  • @author: chenmingyu
  • @date: 2019/4/8 15:23
  • @description: 实现Callable接口
    */

public class CallableTest implements Callable {

@Override
public Integer call() throws Exception {
    IntStream.range(0,10).forEach(i->{
        System.out.println(Thread.currentThread().getName()+":"+i);
    });
    return -1;
}

public static void main(String[] args) throws Exception {
    Callable callable = new CallableTest();
    FutureTask futureTask = new FutureTask(callable);
    new Thread(futureTask,"future").start();
    System.out.println("result:"+futureTask.get());
}

}
线程的暂停,恢复,停止
不安全的线程暂停,恢复,停止操作

Thread提供的过期方法可以实现对线程进行暂停suspend(),恢复resume(),停止stop()的操作

例:创建一个线程,run()中循环输出当前时间,在main()方法中对新建线程进行暂停,恢复,停止的操作

/**

  • @author: chenmingyu
  • @date: 2019/4/8 15:51
  • @description: 线程的暂停,恢复,停止
    */

public class OperationThread implements Runnable{

@Override
public void run() {
    while (true){
        try {
            TimeUnit.SECONDS.sleep(1L);
            System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
        }catch (InterruptedException e){
            System.err.println(e.getMessage());
        }
    }
}

public static void main(String[] args) throws Exception{
    Runnable runnable = new OperationThread();
    Thread thread = new Thread(runnable,"operationThread");
    /**
     * 启动,输出当前时间
     */
    thread.start();
    TimeUnit.SECONDS.sleep(3L);

    /**
     * 线程暂停,不在输出当前时间
     */
    System.out.println("此处暂停:"+LocalTime.now());
    thread.suspend();
    TimeUnit.SECONDS.sleep(3L);

    /**
     * 线程恢复,继续输出当前时间
     */
    System.out.println("此处恢复:"+LocalTime.now());
    thread.resume();
    TimeUnit.SECONDS.sleep(3L);

    /**
     * 线程停止,不在输出当前时间
     */
    thread.stop();
    System.out.println("此处停止:"+LocalTime.now());
    TimeUnit.SECONDS.sleep(3L);
}

}
输出

因为是过期方法,所以不推荐使用,使用suspend()方法后,线程不会释放已经占有的资源,就进入睡眠状态,容易引发死锁问题,而使用stop()方法终结一个线程是不会保证线程的资源正常释放的,可能会导致程序异常

安全的线程暂停,恢复,停止操作

线程安全的暂停,恢复操作可以使用等待/通知机制代替,安全的停止操作可以用线程是否被中断进行判断

安全的线程暂停,恢复(等待/通知机制)

相关方法:

方法名 描述
notify() 通知一个在对象上等待的线程,使其重wait()方法中返回,前提是该线程获得了对象的锁
notifyAll() 通知所有等待在该对象上的线程
wait() 调用该方法线程进入等待状态,只有等待另外线程的通知或被中断才会返回,调用该方法会释放对象的锁
wait(long) 超时等待一段时间(毫秒),如果超过时间就返回
wait(long,int) 对于超时时间耕细粒度的控制,可以达到纳秒
例:创建一个名为waitThread的线程,在run()方法,使用中使用synchronized进行加锁,以变量flag为条件进行while循环,在循环中调用LOCK.wait()方法,此时会释放对象锁,由main()方法获得锁,调用LOCK.notify()方法通知LOCK对象上等待的waitThread线程,将其置为阻塞状态,并将变量flag置为true,当waitThread线程再次获取对象锁之后继续执行余下代码

/**

  • @author: chenmingyu
  • @date: 2019/4/8 20:00
  • @description: wait/notify
    */

public class WaitNotifyTest {

private static Object LOCK = new Object();
private static Boolean FLAG = Boolean.TRUE;
public static void main(String[] args) throws InterruptedException{
    Runnable r = new WaitThread();
    new Thread(r,"waitThread").start();
    TimeUnit.SECONDS.sleep(1L);
    synchronized (LOCK){
        System.out.println(Thread.currentThread().getName()+"唤醒waitThread线程:"+LocalTime.now());
        /**
         * 线程状态由等待状态变为阻塞状态
         */
        LOCK.notify();
        /**
         * 只有当前线程释放对象锁,waitThread获取到LOCK对象的锁之后才会从wait()方法中返回
         */
        TimeUnit.SECONDS.sleep(2L);
        FLAG = Boolean.FALSE;
    }
}

public static class WaitThread implements Runnable {
    @Override
    public void run() {
        /**
         * 加锁
         */
        synchronized (LOCK){
            while (FLAG){
                try {
                    System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
                    /**
                     * 线程状态变为等待状态
                     */
                    LOCK.wait();
                    /**
                     * 再次获得对象锁之后,才会执行
                     */
                    System.out.println(Thread.currentThread().getName()+"被唤醒:"+LocalTime.now());
                }catch (InterruptedException e){
                    System.err.println(e.getMessage());
                }
            }
        }
        System.out.println(Thread.currentThread().getName()+"即将停止:"+LocalTime.now());
    }
}

}
输出

可以看到在mian线程调用LOCK.notify()方法后,沉睡了2s才释放对象锁,waitThread线程在获得对象锁之后执行余下代码

安全的线程停止操作(中断标识)

线程的安全停止操作是利用线程的中断标识来实现,线程的中断属性表示一个运行汇总的线程是否被其他线程进行了中断操作,其他线程通过调用该线程的interrupt()方法对其进行中断操作,而该线程通过检查自身是否被中断来进行响应,当一个线程被中断可以使用Thread.interrupted()方法对当前线程的中断标识位进行复位

例:新建一个线程,run方法中使用Thread.currentThread().isInterrupted()是否中断作为判断条件,在主线程中使用thread.interrupt()方法对子线程进行中断操作,用来达到终止线程的操作,这种方式会让子线程可以去清理资源或一些别的操作,而使用stop()方法则会会直接终止线程

/**

  • @author: chenmingyu
  • @date: 2019/4/8 20:47
  • @description: 中断
    */

public class InterruptTest {


public static void main(String[] args) throws InterruptedException {
    Runnable r = new StopThread();
    Thread thread = new Thread(r,"stopThread");
    thread.start();
    TimeUnit.SECONDS.sleep(1L);
    System.out.println(Thread.currentThread().getName()+"对stopThread线程进行中断:"+LocalTime.now());
    thread.interrupt();
}

public static class StopThread implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
        }
        System.out.println(Thread.currentThread().getName()+"停止:"+LocalTime.now());
    }
}

}
未完待续...
原文地址https://www.cnblogs.com/cmyxn/p/10673601.html

相关文章
|
9天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
8天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
28 9
|
9天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
7天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
10天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
24 2
|
9天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
10天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
22 1
|
5月前
|
Java C++
关于《Java并发编程之线程池十八问》的补充内容
【6月更文挑战第6天】关于《Java并发编程之线程池十八问》的补充内容
49 5
|
2月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
4月前
|
安全 Java 开发者
Java中的并发编程:深入理解线程池
在Java的并发编程中,线程池是管理资源和任务执行的核心。本文将揭示线程池的内部机制,探讨如何高效利用这一工具来优化程序的性能与响应速度。通过具体案例分析,我们将学习如何根据不同的应用场景选择合适的线程池类型及其参数配置,以及如何避免常见的并发陷阱。
56 1