java多线程知识总结

进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

详细解释:
程序:为了完成特定任务,用某种语言编写的一组指令集合(一组静态代码)
进程:运行中的程序,系统调度与资源分配的一个独立单位,操作系统会 为每个进程分配一段内存空间!程序的依次动态执行,经历代码的加载,执行, 执行完毕的完整过程!
线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个 进程中才能执行,线程由程序负责管理,而进程则由系统进行调度!
多线程的理解:并行执行多个条指令,将CPU时间片按照调度算法分配给各个 线程,实际上是分时执行的,只是这个切换的时间很短,用户感觉到"同时"而已!

线程的创建有三种:
1、继承Thread类代表线程
定义Thread类的子类,并重写run()方法,run()方法称为线程的执行体;
创建Thread类的子类的实例,即创建线程的对象;
调用线程对象的start()方法启动线程。
代码示例:

package thread;

public class TestThread {
	public static void main(String[] args) {
		SubThread1 st1 = new SubThread1();
		SubThread1 st2 = new SubThread1();
		st1.start();
		st2.start();
	}
}
class SubThread1 extends Thread{
	public void run(){
		for(int i = 0;i< 14;i++) {
			if(i % 2 == 0) {
				System.out.println(Thread.currentThread().getName()+" "+i);
			}
		}
	}
}

运行结果:

Thread-0 0
Thread-1 0
Thread-1 2
Thread-1 4
Thread-0 2
Thread-1 6
Thread-0 4

2、实现Runnable接口创建线程:
定义Runnable接口实现类,并重写run()方法;
创建Runnable实现类的实例,并将实例对象传给Thread类的target来创建线程对象;
调用线程对象的start()方法启动线程。
代码示例:

package thread;

public class TestRunnable {
         public static void main(String[] args) {
			SubThread2 st = new SubThread2();
			new Thread(st,"thread1").start();
			new Thread(st,"thread2").start();
		}
}
class SubThread2 implements Runnable{
	public void run() {
		for(int i = 0;i < 8; i++) {
			if (i % 2 == 0) {
				System.out.println(Thread.currentThread().getName()+" "+i);
			}
		}	
	}
}

结果:

thread1:0
thread2:0
thread2:2
thread2:4
thread2:6
thread1:2

3、使用Callable接口和Future接口创建线程:
定义Callable接口实现类,指定返回类型,并重写call()方法;
创建Callable接口实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
使用FutureTask对象作为Thread对象的target创建并启动新线程;
调用FutureTask 对象的get()方法来获得子线程执行结束后的返回值。

代码示例:

package thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestCallable {
         public static void main(String[] args) {
			Callable<Integer>myCallable = new SubThread3();
			FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
			for(int i = 0;i < 4;i ++) {
				System.out.println(Thread.currentThread().getName()+" "+i);
				if(i % 2 == 1) {
					Thread thread = new Thread(ft);
					thread.start();
				}
			}
			System.out.println("主线程for循环执行完成");
			try {
				int sum = ft.get();
				System.out.println("sum ="+sum);
			}catch(InterruptedException e){
				e.printStackTrace();
			}catch(ExecutionException e) {
				e.printStackTrace();
			}
		}
}
class SubThread3 implements Callable<Integer>{
	private int i = 0;
	public Integer call()throws Exception{
		int sum = 0;
		for(i = 0;i<3;i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
			sum += i;
		}
		return sum;
	}
}

结果:

main 1
main 2
main 3
主线程for循环执行完成
Thread-1 0
Thread-1 1
Thread-1 2
sum =3

总结:
创建线程必须要通过Thread类的实例或Thread类子类的实例,然后调用start()方法启动线程,上述三种方式可以分为两种:1是调用无参的构造函数Thread()来实例化对象;2、3是调用Thread(Runnable target)构造方法,其中参数为Runnable类的实例化对象,在3中使用了FutureTask(FutureTask实现了Runnable接口),因此可以看做FutureTask的实例化对象是Runnable类型的。

创建线程的三种方式的对比:

  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

  3. 使用Callable 接口的方式有返回值,可以抛出异常。

线程的生命周期和状态转换
在这里插入图片描述
1、新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。此时线程对象在堆空间中分配了一块内存,但还不能运行。

2、就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,java虚拟机会 为它创建调用栈和程序计数器,处于这个状态 的线程位于可运行池中,等待获得CPU的使用权。

3、运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态,每个CPU只能被一个线程占用。只有处于运行状态的线程才可以转换到运行状态,处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

4、阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用CPU等资源之后,该线程就从运行状态进入阻塞状态,进入阻塞状态java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态才有可能分配到CPU,再次进入运行状态。可以分为三种:

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

5、死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。如改线程的run()方法再次执行完成,线程正常结束;线程抛出异常或错误;调用线程的stop()方法结束该线程。一旦线程转换为死亡状态就不能运行且不能转换为其他状态。

线程的调度:
由于一个CPU只能执行一个线程,在运行池中会有多个处于就绪状态的线程在等待CPU,Java虚拟机负责线程的调度,即按照特定的机制为多个线程分配CPU使用权。调度模型分为分时调度模型和抢占式调度模型两种。

猜你喜欢

转载自blog.csdn.net/qq_33699659/article/details/84722019
今日推荐