admin 管理员组

文章数量: 1087603

多线程基本概念
程序
对应于操作系统中的一个可执行文件,启动后将程序加载到内存,开始执行该程序,于是产生了“进程”。
进程
就是执行中的程序,是一个动态的概念。本质是一个在内存中独立运行的程序空间。

  • 进程是程序的一次动态执行过程,占用特定的地址空间。
  • 有三部分组成: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