java 线程基础篇,看这一篇就够了。
前言:
Java三大基础框架:集合,线程,io基本是开发必用,面试必问的核心内容,今天我们讲讲线程。
想要把线程理解透彻,这需要具备很多方面的知识和经验,本篇主要是关于线程基础包括线程状态和常用方法。
本篇主要从线程常用方法来理解线程各个状态及状态的切换,之后再通过状态于状态之间的切换来加深对线程常用方法的应用于印象。
正题:
java中定义了线程的几种状态,在java.lang.Thread.State中,分别为以下6个:
NEW(初始化),RUNNABLE(就绪),BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(超时等待),TERMINATED(终止)
1. 创建:通过继承Thread类,或者实现Runnable接口来创建一个线程。
方式1:继承Java.lang.Thread类,并覆盖run() 方法
优势:编写简单
劣势:单继承的限制----无法继承其它父类,同时不能实现资源共享
方式2:实现Java.lang.Runnable接口,并实现run()方法
优势:可继承其它类,多线程可共享同一个Thread对象
劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法
public class Daqiu extends Thread{
@Override
public void run() {
System.out.println("我打完球了");
}
}
public class chifan implements Runnable{
@Override
public void run() {
System.out.println("我吃完了");
}
}
2.就绪:当线程调用start()方法,会进入准备就绪状态。
start方法是Thread 类的方法,在这个方法中会调用native方法(start0())来让线程就绪,等待CPU为该线程分配资源(时间片)。
3.运行:当线程获得cpu资源(时间片)会执行run()达到正真运行的效果,并可以调用yield()方法试探性的让出cpu执行器权。
1) 上面说了,start()方法最终调用了一个native的方法,并非java实现的,所以这里的run()方法是如何被调用的我们就不研究了。
2) yield()方法是对调度器的一个暗示表示愿意让出CPU执行器的当前使用权,但是调度器可以自由忽略这个提示。此方法使用极少,不过多研究。
3) 要注意,我们手动调用线程的run()方法也会执行run()方法的内容,但是这样就没有达到多线程的效果。这也是run()方法和start()方法的一个重要区别。通过下面的示例可以看出输出的线程名不一样。
public class TestThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args){
TestThread test = new TestThread();
test.start();
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.run();
}
结果:
Thread-0
main
Process finished with exit code 0
4.超时等待:又称为计时等待,让线程等待一定时间。
1) sleep(long): 让当前线程睡眠一定时间后再继续执行,此时只释放资源,不释放锁。这是一个静态的native方法,不过多研究。
2) join(): 该方法有三个重载join(),join(long millis),join(long millis,int nanoseconds),主要看第二个,就是等待一个线程指定毫秒数后再执行。无参数的join方法其实就是调用了join(0),即永远等待下去。不过通过源码我们可以看到,在while循环中有一个条件判断,即isAlive()方法,意思是如果当前线程还活着,就会一直等待下去。
public static native void sleep(long millis) throws InterruptedException;
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
对于join()方法的理解来看一个示例:
public class Chifan extends Thread{
private Daqiu daqiu;
public Chifan(String name,Daqiu daqiu){
super(name);
this.daqiu = daqiu;
}
@Override
public void run() {
try {
daqiu.join();
System.out.println(getName()+"我开始吃饭。。。");
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"我吃完了");
}
}
public class Daqiu extends Thread {
private int playTime;
public Daqiu(String name,int playTime) {
super(name);
this.playTime = playTime;
}
@Override
public void run() {
System.out.println(getName()+"我开始打球了");
try {
sleep(playTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"我打完球了");
}
}
public static void main(String[] args){
Daqiu daqiu = new Daqiu("打球线程:",1000);
Chifan chifan = new Chifan("吃饭线程:",daqiu);
System.out.println("打完球才能吃饭");
daqiu.start();
chifan.start();
}
打完球才能吃饭
打球线程:我开始打球了
打球线程:我打完球了
吃饭线程:我开始吃饭。。。
吃饭线程:我吃完了 Process finished with exit code 0
可以看到:daqiu.join();一定是等打完球了才会执行后面的吃饭。
5.等待:wait()方法只能在synchronized中调用,因为前提是已经拥有某对象锁,但是选择暂时交出去,此时线程将进入等待队列。
Object类中有三个不同参数的wait()方法,如果传入时间参数,也可以理解为计时等待,但于sleep()不同的是wait()方法会释放拥有的锁,当被其他持有该锁的线程调用notify()或notifyAll()唤醒时将进入同步队列去竞争锁。wait()实际也是调用了wait(long)方法,参数为0,表示一直等待下去。
6.阻塞:当synchronized(Obj)去竞争一个对象锁时,如果对象锁被其他线程占用,那么线程将进入等待队列(阻塞)
在java中的Object类型中,都是带有一个内存锁的,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现JAVA中简单的同步、互斥操作。当出现阻塞锁定需要等其他持有该对象锁的线程调用wait(),notify()或notifyAll()释放对象锁后才有机会获取锁进入运行状态,所以wait(),notify()或notifyAll()只能在synchronized获取到对象锁内使用,这于第五点是相呼应的。(注意:调用notify()方法后,当前线程并不会马上释放该对象锁,要等到执行notify()方法的线程执行完才会释放对象锁)
7.终止:当线程run()方法正常执行完毕,或者出现未捕获的异常,线程就已经完成了他的使命进入终止状态了。
public class TestThread extends Thread {
private Student student;
public TestThread(String name,Student student){
super(name);
this.student = student;
}
@Override
public void run() {
synchronized (student){
try {
System.out.println("我是线程:"+Thread.currentThread().getName());
student.wait();
System.out.println("我是线程:"+Thread.currentThread().getName()+",我拿到了对象锁");
System.out.println("我是线程:"+Thread.currentThread().getName()+",我执行完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class NotifyThread extends Thread {
private Student student;
public NotifyThread(String name,Student student){
super(name);
this.student = student;
}
@Override
public void run() {
synchronized (student){
System.out.println("我是唤醒线程"+Thread.currentThread().getName());
student.notifyAll();
System.out.println("我是唤醒线程"+Thread.currentThread().getName()+",我已经执行唤醒。");
}
}
}
public static void main(String[] args) throws InterruptedException {
Student student = new Student("小明","12");
TestThread testA = new TestThread("A",student);
TestThread testB = new TestThread("B",student);
NotifyThread testC = new NotifyThread("C",student);
testA.start();
testB.start();
sleep(10);
testC.start();
}
我是线程:A
我是线程:B
我是唤醒线程C
我是唤醒线程C,我已经执行唤醒。
我是线程:B,我拿到了对象锁
我是线程:B,我执行完了
我是线程:A,我拿到了对象锁
我是线程:A,我执行完了 Process finished with exit code 0
延申一个线程面试中的经典问题:创建3个线程,顺序答应‘A’,‘B’,‘C’各10次
其实思路也是利用wait()和notify()来控制线程的执行顺序,具体实现这里就不说了。
总结:
java中线程还有非常多的延申,包括线程底层实现,多线程同步,并发,安全,唤醒机制,以及线程池,要学习于沉淀的知识非常多。希望本文能让自己对线程的基础有个清晰的理解。
java 线程基础篇,看这一篇就够了。的更多相关文章
- Java入门基础知识点总结(详细篇)
Java入门基础知识点总结(详细篇)~~~~~目录 1.1 图解 1.1.1 Java基础知识点 1.1.2 Java基础语法的相关内容 1.2 关键字 1.3 标识符 1.3.1 标识符概念 1.3 ...
- Java 线程基础
Java 线程基础
- Java 线程基础,从这篇开始
线程作为操作系统中最少调度单位,在当前系统的运行环境中,一般都拥有多核处理器,为了更好的充分利用 CPU,掌握其正确使用方式,能更高效的使程序运行.同时,在 Java 面试中,也是极其重要的一个模块. ...
- Java线程池的了解使用—筑基篇
前言 Java中的线程池是一个很重要的概念,它的应用场景十分广泛,可以被广泛的用于高并发的处理场景.J.U.C提供的线程池:ThreadPoolExecutor类,可以帮助我们管理线程并方便地并行执行 ...
- java线程基础巩固---线程生命周期以及start方法源码剖析
上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...
- java线程基础知识----线程基础知识
不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...
- Java 线程基础知识
前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...
- Java线程基础实例
概述 Java线程是一个在实战开发中经常使用的基础功能,而在Java中线程相关的类在java.lang和java.util.concurrent里 Thread package thread.base ...
- Java线程基础知识(状态、共享与协作)
1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...
随机推荐
- Java大整形BigInteger的用法
基本类型int有32位,范围是:[-2147483648, 2147483647](正负21亿多) 基本类型long有64位,范围是:[-9223372036854775808, 9223372036 ...
- vscode配置java+gradle开发环境
1.安装扩展包Java Extension Pack,里面包含java开发所必须的扩展 2.安装java jdk,8版本就是1.8版本,根据需要安装不同的版本 3.下载gradle,将bin文件夹添加 ...
- dragover event 翻译
当选择的元素或文本被拖拽到一个有效的放置目标上时(每几百毫秒),dragover事件就会被触发. 该事件在放置目标上被触发. Property Type Description target Read ...
- 【干货】WordPress系统级更新,程序升级
[干货]WordPress系统级更新,程序升级 网站技术日新月异,更新升级是维护工作之一,长时间不升级的程序,就如长时间不维护的建筑物一样,会加速老化.功能逐渐缺失直至无法使用.在使用WordPres ...
- SpringBoot系列——动态定时任务
前言 定时器是我们项目中经常会用到的,SpringBoot使用@Scheduled注解可以快速启用一个简单的定时器(详情请看我们之前的博客<SpringBoot系列--定时器>),然而这种 ...
- 使用Freemarker导出Word文档(包含图片)代码实现及总结
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- 客户端连接mysql数据库反应慢
远程客户端连接MysqL数据库太慢解决方案 局域网客户端访问mysql 连接慢问题解决 编辑mysql配置文件 # vi my.conf [mysqld] skip-name-resolve 重启my ...
- idea 2019.3.3 系列产品破解
所有软件版本要求 必须是2019.3.3版本,可破解idea, goland, datagrid, pycharm等系列产品. 编辑vmoptions 添加破解jar包 然后重新打开,输入激活码进行激 ...
- 命令执行 安鸾 Writeup
目录 命令执行 01 命令执行 02 仅代码层面来说,任意命令执行漏洞的利用条件: 1.代码中存在调用系统命令的函数 2.函数中存在我们可控的点 3.可控点没有过滤,或过滤不严格. Linux命令连接 ...
- tomcat下载、安装及配置
一,下载Tomcat 1.进入官网Http://tomcat.apache.org/,选择download,下载所需要的Tomcat版本. 注意有zip和exe两种格式的 zip(64-bit Win ...