java进阶:15.1 多线程 - Thread、Executor

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/L20902/article/details/89186711

1. 进程、线程概念

  1. 首先要理解进程(Processor)和线程(Thread)的区别:
  • 进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。

  • 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。

  1. 一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。

  2. 线程没有地址空间,线程包含在进程的地址空间中。线程上下文只包含一个堆栈、一个寄存器、一个优先权,线程文本包含在他的进程 的文本片段中,进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段, 寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。
     

2. 创建任务和线程

可以在程序中 创建附加的线程 以执行并发任务。在Java 中,每个任务都是 Runnable 接口的一个实例,也称为可运行对象(runnable object) 。线程本质上讲就是便于任务执行的对象。

  1. 定义一个实现Runnable 接口的类
    任务就是对象。为了创建任务,必须首先为任务定义一个实现Runnable 接口的类。Runnable 接口非常简单,它只包含一个run 方法。需要实现这个方法来告诉系统线程将如何运行。

  2. 创建一个任务
    一旦定义了一个TaskClass ,就可以用它的构造方法创建一个任务。

  3. 创建任务的线程:
    任务必须在线程中执行。Thread 类包括创建线程的构造方法以及控制线程的很多有用的方法。使用下面的语句创建任务的线程:
    Thread thread = new Thread(task);

  4. 调用start()方法
    然后调用start()方法告诉Java 虚拟机该线程准备运行。 thread.start();

  5. JVM调用run()方法执行任务


package Thread;
public class test {
	public static void main(String[] args) {
		Runnable printA = new PrintChar('a',100);  // 第 2 步
		Runnable printB = new PrintChar('b',100);
		Runnable print100 = new PrintNum(100);
		
		Thread thread1 = new Thread(printA);  // 第 3 步
		Thread thread2 = new Thread(printB);
		Thread thread3 = new Thread(print100);
		
		thread1.start();   // 第 4 步
		thread2.start();
		thread3.start();
	}
}
	
class PrintChar implements Runnable{  // 第 1 步
	private char charToPrint;
	private int times;
	public PrintChar(char c,int t) {
		charToPrint = c;
		times = t;
	}	
	@Override
	public void run() {
		for(int i = 0; i < times; i++)
			System.out.print(charToPrint);
	}
}
	
class PrintNum implements Runnable{
	private int lastNum;
	public PrintNum(int n) {
		lastNum = n;
	}	
	@Override
	public void run() {
		for(int i = 1; i <= lastNum; i++) {
			System.out.print(" " + i);
		}
	}
}

3. Thread类

在这里插入图片描述
Thread.sleep(1000); 表示当前线程暂停1000毫秒 ,其他线程不受影响
Thread.sleep(1000); 会抛出InterruptedException 中断异常,因为当前线程sleep的时候,有可能被停止,这时就会抛出 InterruptedException

Thread.yield(); 临时暂停,使得其他线程可以占用CPU资源

t1.setDaemon(true); 守护线程就相当于那些支持部门,如果一个进程只剩下守护线程,那么进程就会自动结束。守护线程通常会被用来做日志,性能统计等工作。

4. 优先级

Java 给每个线程指定一个优先级。默认情况下,线程继承生成它的线程的优先级。可以用 setPriority 方法提高、降低线程的优先级,还能用 getPriority 方法获取线程的优先级。

优先级是从1 到10 的数字。

Thread 类有int 型常量 MIN_PRIORITYNORM_PRIORITYMAX_PRIORITY ,分别代表1 、5 和10 。

主线程的优先级是 Thread.NORM_PRIORITY。

例如在上节的历程里修改一下程序:

		thread1.start();
		thread2.start();
		thread3.start();
		
		thread3.setPriority(Thread.MAX_PRIORITY);
	}

则任务print100 的线程首先结束。
 

5. 线程池

顾名思义线程池就是线程的容器。在没有接触线程池之前,我们使用线程的时候就去创建一个线程,然后startup()就可以(#1里提到的)

但是就会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
  
假设,你现在有一个while n(n=10000)的循环,每一次循环都要启动一个线程去计算n的因数,这样频繁而且大量的创建线程,系统的效率会大幅下降,系统会很快奔溃。
  
线程池就可以帮助我们解决这个问题,他使线程可以重复使用,就是执行完一个任务线程不会被销毁,而是可以继续执行其他任务

线程池类ThreadPoolExecutor 在包 java.util.concurrent 下

线程池是管理并发执行任务个数的理想方法。 Java 提供 Executor ( n. 执行者 ) 接口来执行线程池中的任务,提供 ExecutorService 接口来管理和控制任务。ExecutorService 是Executor 的子接口
在这里插入图片描述
为了创建一个Executor 对象,可以使用Executors 类中的静态方法,newFixedThreadPool(int) 方法在池中创建固定数目的线程。
ExecutorService executorService= Executors.newFixedThreadPool(5);

接下来向线程池提交一个任务。
executorService.execute(new TestRunnable());

如果线程完成了任务的执行,它可以被重新使用以执行另外一个任务

如果线程池中所有的线程都不是处于空闲状态,而且有任务在等待执行,那么在关闭之前,如果由于一个错误终止了一个线程,就会创建一个新线程来替代它。如果线程池中所有的线程都不是处于空闲状态,而且有任务在等待执行,那么newCachedThreadPool() 方法就会创建一个新线程。如果缓冲池中的线程在60 秒内都没有被使用就该终止它。对许多小任务而言,一个缓冲池已经足够。

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool——创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool——创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool——创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor——创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

	public static void main(String[] args) {		
		ExecutorService executor = Executors.newFixedThreadPool(3);
		executor.execute(new PrintChar('a',100));
		executor.execute(new PrintChar('b',100));
		executor.execute(new PrintNum(100));
		
		executor.shutdown();
	}

把第2行换成:ExecutorService executor = Executors.newCachedThreadPool();
又会发生什么呢?

将为每个等待的任务创建一个新线程,所以,所有的任务都并发地执行。

最后一行的方法shutdown() 通知执行器关闭。不能接受新的任务,但是现有的任务将继续执行直至完成。

如果仅需要为一个任务创建一个线程,就使用Thread 类。如果需要为多个任务创建线程,最好使用线程池。

这是一位大佬写的关于线程池使用的文章,日后详看!

猜你喜欢

转载自blog.csdn.net/L20902/article/details/89186711