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

相关文章
|
11天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
9天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
11天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
11天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
11天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3
|
11天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
92 2
|
8天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
11天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
35 1
|
3月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
62 1
|
3月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
40 3