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
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java程序运行时内存分析
Java程序运行在Java虚拟机中,JVM把他管理的内存分为三个区域:栈Stack、堆Heap、方法区Method Area 栈描述的是方法执行的内存模块,每个方法的调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量),栈属于线程私有、不能实现线程中的共享,连续空间,速度快。 堆用于存储创建好的对象和数组,JVM只有一个堆,被所有线程共享堆是一个不连续的内存空间。分配灵活,速度慢,new执行完就放进堆。 方法区又叫静态区JVM只有一个方法区,被所有线程共享,方法区实际也是堆,只是用于存储类、常量相关的信息,用来存放程序中永远不变或唯一的内容(类信息【class对象】、代码、静态变量、字符串常量。 package 内存分析; public class Person { int age; String name; Person(int age,String name){ this.age = age; this.name = name; } public static void main(String[] a...
- 下一篇
java B2B2C springmvc mybatis多租户电子商城系统-gateway 限流
Spring Cloud Gateway限流 在Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,适用Redis和lua脚本实现了令牌桶的方式。 具体源码不打算在这里讲述,读者可以自行查看,代码量较少,先以案例的形式来讲解如何在Spring Cloud Gateway中使用内置的限流过滤器工厂来实现限流。 首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <depende...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,CentOS7官方镜像安装Oracle11G