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 在线/离线 文档
英文: Oracle 官网在线文档 Java SE 8 Oracle 官网在线文档 Java SE 9 & JDK 9 中文: Java 1.8: 中文 – 谷歌版 中文 – 有道版 中文 – ...
- npx的使用方法、场景
目录 npx使用教程 npm与npx的概念 npx的使用场景(对比npm的一些优势) 使用场景1: 想用项目中已经安装好的某个包, 但是不能直接执行(因为没有全局安装, 涉及环境变量的问题) 使用场景 ...
- iTop安装 vm虚拟机、Linux、centos7安装itop 2.6.1
itop安装流程,是我基于下面两位博主发布的文章整理出来的,欢迎大家学习,如有错误之处请大家留言指出我看到之后及时更新.谢谢 https://blog.csdn.net/qq_23565543/art ...
- 剑指 Offer 30. 包含min函数的栈
剑指 Offer 30. 包含min函数的栈 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min.push 及 pop 的时间复杂度都是 O(1). 示例 ...
- 腾讯云分布式数据库TDSQL在银行传统核心系统中的应用实践
本文是腾讯云TDSQL首席架构师张文在腾讯云Techo开发者大会现场的演讲实录,演讲主题是<TDSQL在银行传统核心系统中的应用实践>. 我是TDSQL架构师张文,同时也是TDSQL的开发 ...
- 如何根治慢SQL?
本文摘自:CodeSheep 今天和大家聊一个常见的问题:慢SQL. 包括以下内容: 慢SQL的危害 SQL语句的执行过程 存储引擎和索引的那些事儿 慢SQL解决之道 后续均以MySQL默认存储引擎I ...
- Linux进程理解与实践(四)wait函数处理僵尸进程
Wait的背景 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程 ...
- idea 2019.3.3 系列产品破解
所有软件版本要求 必须是2019.3.3版本,可破解idea, goland, datagrid, pycharm等系列产品. 编辑vmoptions 添加破解jar包 然后重新打开,输入激活码进行激 ...
- CTF--[BJDCTF2020]Cookie is so stable 1(SSTI)
从hint.php可以找到提示,要求观察cookies 打开flag.php可以看到需要输入用户名,多次试验后发现输入的用户名会以cookies的方式储存 使用dirsearch扫描没有发现什么有用的 ...
- 伪静态是什么?伪静态与普通html静态网页区别?
什么是伪静态,伪静态作用伪静态即是网站本身是动态网页如.php..asp..aspx等格式动态网页有时这类动态网页还跟"?"加参数来读取数据库内不同资料.很典型的案例即是discu ...