admin 管理员组文章数量: 1087594
多线程基本概念
程序
对应于操作系统中的一个可执行文件,启动后将程序加载到内存,开始执行该程序,于是产生了“进程”。
进程
就是执行中的程序,是一个动态的概念。本质是一个在内存中独立运行的程序空间。
- 进程是程序的一次动态执行过程,占用特定的地址空间。
- 有三部分组成:cpu,data,code。每个进程是独立的,保有自己的cpu时间,代码和数据,缺点是:浪费内存,cpu的负担较重。
- 多任务操作系统将CPU时间动态划分给每个进程,同时执行多个进程,每个进程独立运行。
线程
一个进程产生多个线程。同一进程的多个线程也可以共享此进程的某些资源(如:代码、数据),所以线程又被称为轻量级进程。
- 一个进程内部的一个执行单元,是程序中的一个单一的顺序控制流程。
- 一个进程可拥有多个并行的线程。
- 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且他们从同一堆中分配对象并进行通信、数据交换和同步操作。
- 线程间通信是在同一地址空间上进行的,不需要额外的通信机制,所以线程间传递速度更快。
线程和进程的区别
- 线程在进程中运行
- 一个进程包含多个线程
- 不同进程间数据很难共享,而同一进程下的不同线程间数据很易共享。
- 进程要比线程消耗更多的计算机资源。
- 进程间不会相互影响,因为空间完全隔离,而进程中的一个线程挂掉会导致整个进程挂掉。
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束才能使用这块内存。
- 一个进程如果只有一个线程则可以看作单线程,有多个线程,进程的执行过程不是一条线的,而是多条线共同完成。
什么是主线程已经子线程
主线程
当java程序启动时,一个线程会立刻运行,该线程叫做程序的主线程,即main方法对应的线程,是程序开始时执行的。
main方法是一个应用的入口,代表了应用的主线程。JVM执行main方法时,main方法会进入到栈内存,JVM会通过操作系统开辟一条main方法通向cpu执行路径,cpu就会通过这路径执行main方法,这个路径叫main(主)线程。
主线程特点:
是产生其他子线程的线程。
不一定是最后完成执行的线程,子线程可能在他接受后还在运行。
子线程
在主线程中创建并启动的线程一般称为子线程。
线程创建
1、通过继承Thread类实现多线程
java.lang.Thread类是负责实现多线程的类。
继承Thread类实现多线程的步骤:
- 继承Thread类定义线程类。
- 重写Thread类中的run()方法。run()方法也称为线程体。
- 实例化线程类并通过start()方法启动线程
public class TestThread extends Thread {
public TestThread(){
System.out.println(this.getName());
}
/**
* 线程的线程体
*/
@Override
public void run() {
System.out.println(this.getName()+"线程开始");
for(int i=0;i<20;i++){
System.out.println(this.getName()+" "+i);
}
System.out.println(this.getName()+"线程结束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
TestThread t1 = new TestThread();
//启动线程
t1.start();
TestThread t2 = new TestThread();
//启动线程
t2.start();
System.out.println("主线程结束");
}
}
2、通过实现Runnable接口实现多线程
public class TestThread2 implements Runnable {
public TestThread2(){
System.out.println(Thread.currentThread().getName());
}
/**
*当前线程的线程体方法
*/
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始");
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
TestThread2 testThread2 = new TestThread2();
Thread t1 = new Thread(testThread2);
t1.start();
Thread t2 = new Thread(new TestThread2());
t2.start();
System.out.println("主线程结束");
}
}
线程执行流程
线程生命周期
一个线程对象在它的生命周期内,需要经历5个状态。
-
新生状态(New)
用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。 -
就绪状态(Runnable)
处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。,当系统选定一个等待执行
的Thread 对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:- 新建线程:调用start()方法,进入就绪状态;
- 阻塞线程:阻塞解除,进入就绪状态;
- 运行线程:调用yield()方法,直接进入就绪状态;
- 运行线程:JVM将CPU资源从本线程切换到其他线程。
-
运行状态(Running)
执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而堵塞或完成任务而死亡。如果在时间片内没有执行结束,会被系统换下来到就绪状态。 -
阻塞状态(Blocked)
指的是暂停一个线程的执行以等待某个条件发生。4种原因导致阻塞:- 执行sleep方法,使当前线程休眠,进入阻塞状态。指定时间到了后进入就绪状态。
- 执行wait()方法,进入阻塞状态。当使用nofity()方法唤醒线程后进入就绪状态。
- 线程运行时某个操作进入阻塞状态,比如执行IO流操作(read()/write()本身就是阻塞的方法)。只有引起阻塞的原因消失后线程才进入就绪状态。
- join()线程联合:某个线程等待另一个线程执行结束后才能继续执行时,使用join()方法。
-
死亡状态(Terminated)
线程生命周期的最后一个阶段。死亡原因有两个:
1、正常运行的线程完成了run()方法内的全部工作;
2、线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程(这两个方法已被JDK废弃,不推荐使用)
当一个线程进入死亡状态后就不能回到其他状态了。
线程使用
终止线程
由于stop()或destroy()方法已被JDK废弃,通常终止线程的方法是提供一个boolean型的终止变量,当这个变量为false时,终止线程的执行。
public class StopThread implements Runnable {
private boolean flag = true;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开始");
int i= 0;
while(flag){
System.out.println(Thread.currentThread().getName()+" "+i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
public void stop(){
this.flag = false;
}
public static void main(String[] args)throws Exception {
System.out.println("主线程开始");
StopThread st = new StopThread();
Thread t1 = new Thread(st);
t1.start();
System.in.read();
st.stop();
System.out.println("主线程结束");
}
}
暂停当前线程执行sleep/yield
两个方法区别:
- sleep()方法:让正在运行的线程进入阻塞状态,知道休眠时间满,进入就绪状态。
- yield()方法:让正在运行的线程进入就绪状态,让出CPU使用权。
sleep方法使用
public class SleepThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开始");
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
Thread t = new Thread(new SleepThread());
t.start();
System.out.println("主线程结束");
}
}
yield方法使用
作用:暂停当前执行线程,并执行其他线程。
目的是让具有相同优先级的线程之间能够适当的轮换执行。实际中无法保证 yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
注意:
- yield 是一个静态的方法。
- 调用 yield 后,yield 告诉当前线程把运行机会交给具有相同优先级的线程。
- yield 不能保证,当前线程迅速从运行状态切换到就绪状态。
- yield 只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。
public class YieldThread implements Runnable {
@Override
public void run() {
for(int i=0;i<30;i++){
if("Thread-0".equals(Thread.currentThread().getName())){
if(i == 0){
Thread.yield();
}
}
System.out.println(Thread.currentThread().getName()+""+i);
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new YieldThread());
Thread t2 = new Thread(new YieldThread());
t1.start();
t2.start();
}
}
线程的联合
线程A在中可以调用线程B的join()方法,让线程B和线程A联合,线程A必须等待线程B执行完毕后,才能继续执行。
join方法的使用
指调用该方法的线程在执行完run()方法后,在执行join 方法后面的代码,即将两个线程合并,用于实现同步控制。
class A implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+""+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class B implements Runnable{
@Override
public void run
版权声明:本文标题:JAVA学习--多线程 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/b/1749725451a2754121.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论