多线程及线程池

1. 多线程

1.1     多线程介绍

进程:进程指正在运行的程序。( 如正在运行的QQ为一个进程)。 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行, 一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为 多线程程序。( 如正在运行的QQ中的好友通话为一个线程,QQ空间又为一个线程)。

1.2 线程的运行模式

a:分时调度
      所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。   
b: 抢占式调度
     优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个( 线程随机性), Java使用的为抢占式调度
并发性和并行性的区别:并行指的是在同一时刻,有多条指令在处理器上同时执行,而并发指的是在同一时刻只能有一条指令执行,但是有多个线程指令被极快速的轮换执行。    实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。( 并发)其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

1.3主线程

jvm启动后,必然有一个执行路径(线程)main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行。但是我们可以通过主线程负责执行其中一个循环,再由另一个线程负责其他代码的执行,最终实现多部分代码同时执行的效果。

1.4创建线程的三种方法

1.4.1 继承Thread类

创建线程的步骤:

1 定义一个类继承Thread。

2 重写run方法。

3 创建子类对象,就是创建线程对象。

4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法

/*
 *  定义子类,继承Thread 
 *  重写方法run 
 */
public class SubThread  extends Thread{
	public void run(){
		for(int i = 0; i < 50;i++){
			System.out.println("run..."+i);
		}
	}
}
/*
 * 创建和启动一个线程
 *   创建Thread子类对象
 *   子类对象调用方法start()
 *      让线程程序执行,JVM调用线程中的run
 */
public class ThreadDemo {
	public static void main(String[] args) {
		SubThread st = new SubThread();
		SubThread st1 = new SubThread();
		st.start();
		st1.start();
		for(int i = 0; i < 50;i++){
			System.out.println("main..."+i);
		}
	}
}

1.4.2实现Runnable接口

创建线程的步骤。

1、定义类实现Runnable接口。

2、覆盖接口中的run方法。。

3、创建Thread类的对象

4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

5、调用Thread类的start方法开启线程。


/*
 *  实现线程成功的另一个方式,接口实现
 *  实现接口Runnable,重写run方法
 */
public class SubRunnable implements Runnable{
	public void run(){
		for(int i = 0 ; i < 50; i++){
			System.out.println("run..."+i);
		}
	}
}
/*
 *  实现接口方式的线程
 *    创建Thread类对象,构造方法中,传递Runnable接口实现类
 *    调用Thread类方法start()
 */
public class ThreadDemo {
	public static void main(String[] args) {
		SubRunnable sr = new SubRunnable();
		Thread t = new Thread(sr);
		t.start();
		for(int i = 0 ; i < 50; i++){
			System.out.println("main..."+i);
		}
	}
}

1.4.3使用Callable和Future创建线程

步骤
1.创建Callable接口的实现类,并实现call()方法(为线程的执行体),且该call()方法有返回值,在创建Callable实现类的实例。
2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了call()方法的返回值
3.使用FutureTask对象作为Thread对象的target创建并启动新线程
4.调用FutureTask对象的get()方法获取返回值。

public class MyCallable implements Callable<Integer> {
	//成员变量
	int x = 5;
	int y = 3;
	//构造方法
	public MyCallable(){
	}
	public MyCallable(int x, int y){
		this.x = x;
		this.y = y;
	}

	@Override
	public Integer call() throws Exception {
		return x+y;
	}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class test {

	public static void main(String[] args) throws Exception {
		//1.创建Callable接口的实现类的实例
		Callable<Integer> mc=new MyCallable();
		//创建FutureTask实例
		FutureTask<Integer> ft=new FutureTask<>(mc);
		//创建thread实例
		Thread th=new Thread(ft);
		th.start();
		System.out.println(ft.get());
	}
}

1.5线程的匿名内部类的使用

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

l方式1:创建线程对象时,直接重写Thread类中的run方法

new Thread() {
			public void run() {
				for (int x = 0; x < 40; x++) {
					System.out.println(Thread.currentThread().getName()
							+ "...X...." + x);
				}
			}
		}.start();

l  方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法

Runnable r = new Runnable() {
			public void run() {
				for (int x = 0; x < 40; x++) {
					System.out.println(Thread.currentThread().getName()
							+ "...Y...." + x);
				}
			}
		};
		new Thread(r).start();

2.线程池

2.1     线程池概念

线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源

2.2 线程池原理

1.在java中,如果每个请求到达就创建一个新线程,开销是相当大的。
2.在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。

 3.除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

2.3创建线程池方法

2.3     使用线程池方式--Runnable接口

通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。                                                                                                                                    

 Executors:线程池创建工厂类

 public static ExecutorServicenewFixedThreadPool(int nThreads):返回线程池对象

ExecutorService:线程池类

 Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

使用线程池中线程对象的步骤:

创建线程池对象

 创建Runnable接口子类对象

提交Runnable接口子类对象

关闭线程池
public class ThreadPoolDemo {
	public static void main(String[] args) {
		//创建线程池对象
		ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
		//创建Runnable实例对象
		MyRunnable r = new MyRunnable();
		
		//自己创建线程对象的方式
		//Thread t = new Thread(r);
		//t.start(); ---> 调用MyRunnable中的run()
		
		//从线程池中获取线程对象,然后调用MyRunnable中的run()
		service.submit(r);
		//再获取个线程对象,调用MyRunnable中的run()
		service.submit(r);
		service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

//关闭线程池
		//service.shutdown();
	}

2.4  使用线程池方式—Callable接口

Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。

ExecutorService:线程池类

<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法

Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

使用线程池中线程对象的步骤:

 创建线程池对象

创建Callable接口子类对象

 提交Callable接口子类对象

 关闭线程池

public class ThreadPoolDemo {
	public static void main(String[] args) {
		//创建线程池对象
		ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
		//创建Callable对象
		MyCallable c = new MyCallable();
		
		//从线程池中获取线程对象,然后调用MyRunnable中的run()
		service.submit(c);
		
		//再获取个教练
		service.submit(c);
		service.submit(c);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

//关闭线程池
		//service.shutdown();
	}
}




猜你喜欢

转载自blog.csdn.net/naruto_c/article/details/78608349