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调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...
随机推荐
- S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月
S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月 本文档的创建作者的腾讯QQ聊天号码是 595076941 S02-电脑软件安装过程中不要随意关闭窗口除非必需关闭窗口 ...
- 关于表单重复提交之验证码 和谷歌Kaptcha图片验证码的使用
表单重复提交之-----验证码 表单重复提交有三种常见的情况: 一:提交完表单.服务器使用请求转来进行页面跳转.这个时候,用户按下功能键 F5,就会发起最后一次的请求. 造成表单重复提交问题.解决方法 ...
- 跟我一起写 Makefile(十二)
隐含规则 ---- 在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o]文件,Windows下是[.obj ...
- Create Virtual Network with Virtualbox
Create a virtual machine "ubs1" with ubuntu server 12.04, set its network as Host-only; St ...
- Go连接到Linux服务器进行操作-执行shell命令&&上传文件
Go连接到Linux服务器进行操作 使用密码连接Linux服务器 package main import ( "fmt" "golang.org/x/crypto/ssh ...
- MySQL-16-主从复制进阶
延时从库 介绍 延时从库: 是我们人为配置的一种特殊从库,人为配置从库和主库延时N小时 为什么要有延时从库 数据库故障 物理损坏,普通的主从复制非常擅长解决物理损坏 逻辑损坏,普通主从复制没办法解决逻 ...
- 安全工具推荐之Goby篇
Goby(鰕虎鱼) 这个东西出来也很久了,有一年多了吧,个人感觉用起来还不错(当然见仁见智哈,别喷我),今天拿来水一篇 官网有很详细的使用说明,所以本文纯属发表一下感慨,非技术贴 官网在此:https ...
- Android WorkManager工作约束,延迟与查询工作
WorkManager工作约束,延迟与查询工作 本文可能会混用"工作"与"任务"这两个词. 本文例子使用Kotlin 准备一个工作类(任务)UploadWork ...
- AWS(amazon ec2)服务器流量查询
aws ec2流量监控 亚马逊云服务新用户绑定信用卡免费使用一年,相信很多人白嫖过,选用micro最低配置+流量免费15G,包含上下行.这种配置用来测试玩玩还行,生产使用的话容易超标.很多人想知道流量 ...
- 【硬件模块】UWB介绍
From: https://liudongdong1.github.io/ UWB超宽带定位技术属于无线定位技术的一种.无线定位技术是指用来判定移动用户位置的测量方法和计算方法,即定位算法.目前最常用 ...