一、线程相关概念:
1、什么是进程
进程(Process):进程是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位。
可以简单理解为:进程是正在操作系统中运行的一个程序。
2、什么是线程
线程(thread):线程是进程的一个执行单元。
一个线程就是进程中一个单一顺序的控制流,是进程的一个分支。
进程与线程之间的关系:
- 进程是线程的容器,一个进程至少有一个线程,一个进程也可以有多个线程。
- 在操作系统中是以进程为单位分配内存,同一个进程的所有线程共享该进程所有资源。每个线程都有自己的线程栈,寄存器环境和本地内存。
- CPU分配给线程,即真正在处理器运行的是线程。
3、串行、并发、并行的概念
串行:一个执行完再执行下一个,同一时间只有一个任务在执行。
并发:一段时间多个任务都在执行
并行:多个任务同时开始,是并发的特殊条件。
4、并行的原理
并行的原理是:时间片轮询技术
- 如果单核 CPU,一个处理器一次只能执行一个线程的情况下,处理器可以使用时间片轮询技术 ,可以让 CPU 快速的在各个线程之间进行切换,对于用来来说,感觉是三个线程在同时执行。
- 如果是多核心CPU,可以为不同的线程分配不同的 CPU 内核。
二、创建线程的方法有哪些
创建线程的方法有四个,分别是:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 使用线程池创建线程
三、线程有哪些常用方法
一、currentThread()方法
-
Thread.currentThread()
方法可以获得执行当前代码的线程 - 同一段代码可能被不同的线程执行, 因此当前线程是相对的,
Thread.currentThread()
方法的返回值是在代码实际运行时候的线程对象
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
},"嘉然今天没得吃");
thread.start();
运行结果为:
Thread[嘉然今天没得吃,5,main]
二、setName()/getName()方法
-
thread.setName
(线程名):设置线程名称 -
thread.getName()
:获得线程名称
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread.setName("嘉然今天没得吃");
thread.start();
}
运行结果:
嘉然今天没得吃
三、isAlive()方法
-
thread.isAlive()
:方法用于判断当前线程是否启动,且尚未中止。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
}
},"嘉然今天没得吃");
thread.start();
System.out.println("获取线程当前是否处于活动状态:"+thread.isAlive());
}
运行结果:
获取线程当前是否处于活动状态:true
四、sleep()方法
-
thread.sleep(millis)
:让当前的线程休眠一段时间
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("向晚大魔王");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"嘉然今天没得吃");
thread.start();
}
1秒后得到运行结果:
向晚大魔王
五、getID()方法
-
thread.getId()
可以获得线程的唯一标识 - 某个编号的线程运行结束后,该编号可能被后续创建的线程使用 重启的 JVM 后,同一个线程的编号可能不一样。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("当前线程ID为:"+Thread.currentThread().getId());
}
},"嘉然今天没得吃");
thread.start();
}
运行结果:
当前线程ID为:9
六、interrupt()方法
-
interrupt()
:调用 interrupt()方法仅仅是在当前线程打一个中断标志,并不是真正的中断线程 - interrupt()方法的思想是,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。
- 若要停止线程,需要先判断是否有中断标志,若有就使用return中断线程。
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0; i
运行结果:
嘉然+1 :1个嘉然
……
嘉然+1 :21个嘉然
并没有完整循环1000次,说明被中断了
七、setDaemon() 方法
Java 中的线程分为用户线程与守护线程(也叫后台线程)
- 守护线程是为其他线程提供服务的线程,如垃圾回收器(GC)就是一个典型的守护线程。
- 守护线程不能单独运行,当 JVM 中没有非守护用户线程运行,只有守护线程运行时,守护线程会自动销毁,JVM 会退出
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; ; i++) {
System.out.println("嘉然+1 :"+(i+1)+"个嘉然");
}
}
});
thread.setDaemon(true);//设置为守护线程
thread.start();
Thread.sleep(1);
}
运行结果:
嘉然+1 :1个嘉然
……
嘉然+1 :61个嘉然
当main线程结束,守护线程也自动销毁,结束运行。
八、join()方法
-
join()
:方法是让调用该方法的线程在执行完run()方法后,再执行join()方法后面的代码。
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1:"+Thread.currentThread().getName());
},"嘉然今天没得吃");
thread1.start();
Thread thread2 = new Thread(()->{
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2:"+Thread.currentThread().getName());
},"向晚大魔王");
thread2.start();
}
运行结果:
线程1:嘉然今天没得吃
线程2:向晚大魔王线程1休眠了1s,但还是先输出,因为线程1调了join(),所以先执行
四、线程的生命周期
线程的生命周期指的是线程从new创建到运行结束销毁的过程
分为以下六个过程:
-
线程创建之后它将处于 NEW(新建) 状态
-
调用
start()
方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。
-
当线程执行
wait()
方法之后,线程进入 WAITING(等待) 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态。 -
而 TIMED_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过
sleep(long millis)
方法或wait(long millis)
方法可以将 Java 线程置于 TIMED_WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。 -
当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
-
线程在执行 Runnable 的
run()
方法之后将会进入到 TERMINATED(终止) 状态
五、多线程编程的优势以及风险
优势:
- 提高系统的吞吐率(Throughout):多线程编程可以使一个进程有多个并发(concurrent,即同时进行的)的操作。
- 提高响应性(Responsiveness):Web 服务器会采用一些专门的线程负责用户的请求处理,缩短了用户的等待时间。
- 充分利用多核(Multicore)处理器资源,通过多线程可以充分的利用 CPU 资源。
问题与风险:
-
线程安全(Thread safe)问题.多线程共享数据时,如果没有采取正确的并发访问控制措施,就可能会产生数据一致性问题,如读取脏数据(过期的数据),如丢失数据更新。
-
线程活性(thread liveness)问题:由于程序自身的缺陷或者由资源稀缺性导致线程一直处于非 RUNNABLE 状态,这就是线程活性问题。
【信息由网络或者个人提供,如有涉及版权请联系COOY资源网邮箱处理】
暂无评论内容