Java并发(一):线程
前言:
本文将简单的介绍JAVA并发中的线程。
操作系统的多任务(multitasking):计算机在同一刻运行多个程序的能力,即并发。并发执行的进程数目并不是由CPU数目制约的,操作系统将CPU时间片给每一个进程,给人并行处理的感觉。多线程程序在较低层次扩展了多任务的概念:一个程序可以同时处理多个任务。通常,每一个任务称为一个进程。
进程与线程的区别:本质的区别在于每一个进程拥有自己的一整套变量,而线程共享数据。共享数据会在同步时带来风险,然而共享变量使线程之间的通信比进程之间的通信更有效、更容易。与进程相比,线程更加“轻量级”,创建、撤销一个线程比启动新进程的开销要小得多。
一、什么是线程
单独的线程中执行一个任务的简单过程:
1)将任务代码移到实现了Runnable接口的类中:Runnable
接口十分简单,只需要实现一个run
方法
public interface Runnable{
void run();
}
2)由Runnable
创建一个Thread
对象
Thread thread = new Thread(runnable);
3)启动线程 thread.start();
不要调用Tread类或Runnable
对象的run方法。直接调用run
方法,只会执行一个线程中的任务,而不会启动新线程。应该调用Thread.run()
方法。这样将会创建一个新线程来执行run
方法。
二、中断线程
线程终止:当线程的run方法执行方法体中最后一条语句时,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程终止。在早期Java版本中,有一个stop方法可以被其他线程调用它终止线程,这方法已被弃用,不推荐使用。
现在没有强制停止线程的方法,但可以通过interrupt
(中断)方法发送请求终止线程。当线程调用这方法是,线程的中断状态将被置位,这是每一个线程都具有的boolean标志。每个线程都会不时的检查这个标志,以判断线程是否被请求中断。
想要知道当前线程是否被置位,首先应调用静态的Thread.currentThread
方法来获得当前线程,然后调用isInterrupt
方法:
while(!Thread.currentThread().isInterrupt() && more work to do){
do more work;
}
但是,当一个线程被阻塞,就无法检测中断状态。当阻塞和中断请求碰撞在一起时,会产生InterruptedException
。当在一个被阻塞的的线程(线程调用sleep或wait会被阻塞)上调用interrupt时,阻塞调用将会被InterruptedException
中断。
没有任何语言方面的需求要求一个被中断的线程应该终止。中断一个线程不过是引起它的注意。被中断的线程可以决定如何响应中断。某些重要的线程应该处理完异常后,继续执行,而不会理会中断。但更普遍的情况是,线程简单的将中断作为一个终止的请求。这种现成的run方法如下:
Runable r = () ->{
try{
...
while(!Thread.currentThread().isInterrupt() && more work to do){
do more work;
}
}catch(InterruptedException e){
//thread was interrupted during sleeo or wiat
}finally{
//cleanup,if required
}
//exiting thr run method terminates the thread
}
如果在每次工作迭代之后都调用sleep方法(或其他的可中断方法),inInterrupt
检测既没有必要也没有用处。如果在中断状态被置位是调用sleep方法,线程不会休眠。相反,它将会清除这一状态并抛出InterruptedException
异常。
相似的方法:
interrupted
和isInterrupt
interrupted
方法是一个静态方法,它检测当前的线程是否被中断,调用interrupted
方法会清除该线程的中断状态。
isInterrupt
方法是一个实例方法,可用来检测是否有线程被中断,调用这个方法不会改变中断状态。
s
void interrupt()
向线程发送中断请求。将线程中断状态设置为true。如果当前线程被
sleep
或wait
调用阻塞,那么InterruptedException
异常被抛出static boolean interrupted()
测试当前线程是否被中断,静态方法。调用这方法会产生另一作用,它将当前线程的中断状态重置为false。
boolean isInterrupt()
测试线程是否被终止,不会改变中断状态。
static Thread currentThread()
返回代表当前执行线程的Thread对象。
三、线程状态
线程有六种状态:
- New 新创建
- Runnable 可运行
- Blocked 被阻塞
- Waiting 等待
- Timed waiting 计时等待
- Terminated 被终止
通过调用getState
方法可确定一个线程的当前状态
新建状态
当new Thread一个新线程时,改线程还没有还是运行,意味这它的状态是new,当线程处于这一状态时,程序不会开始执行线程中的代码。
Thread thread = new Thread(runnable)
可运行线程
当调用线程的
start
方法,线程处于runnable可运行状态。要注意,一个可运行的线程可能正在运行也可能没有运行,线程的运行取决于操作系统给线程提供运行的时间。系统会从处于可运行状态的线程队列中调度线程,为其提供CPU时间片。 我们将这一状态的线程称为可运行而不是运行。一旦一个线程开始运行,它不必始终保持运行。运行中的线程被中断,为的是让其他线程获得运行机会。
线程调度的细节依赖于操作系统提供的服务。现在所有的桌面以及服务器操作系统都使用抢占式调度。抢占式调度系统给每一个可运行线程一个时间片来执行程序。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。在选择下一个线程时,操作系统还会考虑线程的优先级。
被阻塞和等待线程
当线程处于被阻塞或等待状态时,它不运行任何代码且消耗最少的资源。
- 当一个线程试图获取一个内部的对象锁(而不是
java.util.concurrent
库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。只有当该锁被持有的线程释放时,所有请求该锁的线程变为非阻塞状态,并竞争该锁。 - 当线程等待另一个线程通知调度器的一个条件时,它自己进入等待状态。在调用Object.wait方法或
Thread
,jion
方法,或者是等待java.util.concurrent
库中的Lock
或Condition
时,就会出现线程等待。 - 有几个方法中有一个超时参数。调用他们导致线程进入计时等待状态。这一状态将保持到超时期满或者收到适当的通知。
- 当一个线程试图获取一个内部的对象锁(而不是
被终止的线程
线程有如下两个原因之一而被终止:
因为run方法正常退出而自然死亡
因为一个没有捕获的异常终止了run方法而意外死亡
四、线程属性
线程的属性包括:线程优先级,守护线程,线程组以及处理未捕获异常的处理器。
线程优先级
在Java中,每一个线程有一个优先级,默认情况加,一个线程继承它的父线程的优先级。
可用setPriority方法提高或降低任何一个线程的优先级。。可以将优先级设置在MIN_PRIORITY(1)与MAX_PRIORITY(10)之间的任何值。NORM_PRIORITY被定义为5。每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。但是,线程的优先级是高度依赖于操作系统的。当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台的优先级上。
在设置线程优先级时,要防止线程饿死。每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中进行选择,尽管这样会使低优先级的线程完全饿死。
守护线程
通过调用:
t.setDaemon(true);
将线程设置为守护线程。
守护线程的唯一作用就是为其他线程提供服务。当只剩下守护线程时。虚拟机就会退出。
未捕获异常处理器
线程run方法不能抛出任何的受查异常,而受查异常又会导致线程终止,此时县城就死亡了。有有种办法,不需要任何catch子句来处理被传播的异常。在线程死亡之前,异常被传递到一个用于捕获异常的处理器。
捕获异常的处理器必须属于一个实现
Thread.UncaughtExceptionHandler
接口的类,接口内只有一个方法。
void uncaughtException(Thread t, Throwable e);
可以用
setUncaughtExceptionHandler
方法为任何线程安装处理器。也可以用setDefauktUncaughtHandler
为所有线程安装一个默认的处理器。如果没有,默认的处理器为空。
Java并发(一):线程的更多相关文章
- Java 并发 中断线程
Java 并发 中断线程 @author ixenos 对Runnable.run()方法的三种处置情况 1.在Runnable.run()方法的中间中断它 2.等待该方法到达对cancel标志的测试 ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- java并发编程 线程基础
java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...
- Java并发1——线程创建、启动、生命周期与线程控制
内容提要: 线程与进程 为什么要使用多线程/进程?线程与进程的区别?线程对比进程的优势?Java中有多进程吗? 线程的创建与启动 线程的创建有哪几种方式?它们之间有什么区别? 线程的生命周期与线程控制 ...
- java并发:线程同步机制之Volatile关键字&原子操作Atomic
volatile关键字 volatile是一个特殊的修饰符,只有成员变量才能使用它,与Synchronized及ReentrantLock等提供的互斥相比,Synchronized保证了Synchro ...
- java并发:线程池、饱和策略、定制、扩展
一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...
- java并发:线程同步机制之Lock
一.初识Lock Lock是一个接口,提供了无条件的.可轮询的.定时的.可中断的锁获取操作,所有加锁和解锁的方法都是显式的,其包路径是:java.util.concurrent.locks.Lock, ...
- Java并发编程:线程间通信wait、notify
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java并发编程:线程和进程的创建(转)
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- Java并发3-多线程面试题
1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. 2) 线程和进程有什 ...
随机推荐
- C++ CComboBox控件详解
转载:http://blog.sina.com.cn/s/blog_46d93f190100m395.html C++ CComboBox控件详解 (2010-09-14 14:03:44) 转载▼ ...
- 【题解】[AHOI2013]作业
Link 题目大意:\(n\)个数,\(m\)个询问,每次四个参数,\(l,r,a,b\),问区间\([l,r]\)中出现过的,数值在\([a,b]\)区间中的数的个数以及区间\([l,r]\)中数值 ...
- 【题解】CF1290B Irreducible Anagrams
Link 题目大意:对于一个字符串,每次询问一个区间,看看这个区间是不是可以划分为若干区间,这些区间内数字经过排列后可以还原原来区间. \(\text{Solution:}\) 菜鸡笔者字符串构造该好 ...
- Linux为STDOUT的关键字设置颜色
echo "颜色测试aaa实测" | perl -pe 's/(aaa|实|测)/\e[1;31m$1\e[0m/g'
- Django ORM 引发的数据库 N+1 性能问题
背景描述 最近在使用 Django 时,发现当调用 api 后,在数据库同一个进程下的事务中,出现了大量的数据库查询语句.调查后发现,是由于 Django ORM 的机制所引起. Django Obj ...
- Windows 10 系统 - business editions 和 consumer editions 的区别
我们在使用微软操作系统(Windows 10)的时候,因为系统版本太多导致我们不知道如何选择.对于 Windows 10 系统,应该下载安装 business 还是 consumer 版本这个问题,这 ...
- mongoose 查询数据属性为数组,且包含某个值的方法
mongoose在创建schema的时候有些属性需要设置为数组类型,比如商品图片.商品标签.不同尺寸.价格等. 那么怎么查询具有某个标签的商品了,下面记录一下两种情况: 查询具有'vue'标签的文章 ...
- 如何部署MongoDB并开启远程访问Docker版
Docker安装 安装方法 pull最新版本mongo docker pull mongo 运行 --name设置名称 -v挂载数据 -p端口映射 -d后台运行 mkdir ~/mongo #随便啦自 ...
- 多测师_肖sir_git _004(版本控制器)
gitgit 是一个开源的分布式版本控制系统,用于敏捷高效的处理任何大小的项目.git是linux torvalds 为了帮助管理linux内核开发的一个开放源码的版本控制软件.git与常用的版本控制 ...
- 【自学编程】C语言编程简单的小程序,计算长方体体积!
计算长方体体积 有朋友会说长方体体积还不好算吗?长X宽X高.没错用计算器一下就可以出结果,编程反而麻烦些,但是我们说的是这种思维,如果复杂的重复运算的话写好程序就非常简单了. 简单运算下一个固定高度的 ...