多线程1(Thread,Runnable,线程创建,线程池)
⼀、多线程
1.1 Thread类
该如何创建线程呢?通过API中搜索,查到Thread类。通过阅读Thread类中的描述。Thread是程序中的执⾏线程。Java 虚拟机允许应⽤程序并发地运⾏多个执⾏线程。
构造⽅法
常⽤⽅法
继续阅读,发现创建新执⾏线程有两种⽅法。
⼀种⽅法是将类声明为 Thread 的⼦类。该⼦类应重写 Thread 类的 run ⽅法。创建对象,开启线程。run⽅法相当于其他线程的main ⽅法。
另⼀种⽅法是声明⼀个实现 Runnable 接⼝的类。该类然后实现 run ⽅法。然后创建Runnable的⼦类对象,传⼊到某个线程的构造⽅法中,开启线程。
1.2 创建线程⽅式⼀继承Thread类
创建线程的步骤:
1 定义⼀个类继承Thread。
2 重写run⽅法。
3 创建⼦类对象,就是创建线程对象。
4 调⽤start⽅法,开启线程并让线程执⾏,同时还会告诉jvm去调⽤run⽅法。
测试类:
1/*
2 * 创建和启动⼀个线程
3 * 创建Thread⼦类对象
4 * ⼦类对象调⽤⽅法start()
5 * 让线程程序执⾏,JVM调⽤线程中的run
6*/
7public class ThreadDemo {
8public static void main(String[] args) {
9 SubThread st = new SubThread();
10 SubThread st1 = new SubThread();
11 st.start();
12 st1.start();
13for(int i = 0; i < 50;i++){
14 System.out.println(""+i);
15 }
16 }
17 }
⾃定义线程类,继承thread
1/*
2 * 定义⼦类,继承Thread
3 * 重写⽅法run
4*/
5public class SubThread extends Thread{
6public void run(){
7for(int i = 0; i < 50;i++){
8 System.out.println(""+i);
9 }
10 }
11 }
思考:线程对象调⽤ run⽅法和调⽤start⽅法区别?
线程对象调⽤run⽅法不开启线程。仅是对象调⽤⽅法。线程对象调⽤start开启线程,并让jvm调⽤run⽅法在开启的线程中执⾏。
1.2.1 继承Thread类原理
我们为什么要继承Thread类,并调⽤其的start⽅法才能开启线程呢?
继承Thread类:因为Thread类⽤来描述线程,具备线程应该有功能。那为什么不直接创建Thread类的对象呢?如下代码:
Thread t1 = new Thread();
t1.start();//这样做没有错,但是该start调⽤的是Thread类中的run⽅法,⽽这个run⽅法没有做什么事情,
更重要的是这个run⽅法中并没有定义我们需要让线程执⾏的代码。
创建线程的⽬的是什么?
是为了建⽴程序单独的执⾏路径,让多部分代码实现同时执⾏。也就是说线程创建并执⾏需要给定线程要执⾏的任务。
对于之前所讲的主线程,它的任务定义在main函数中。⾃定义线程需要执⾏的任务都定义在run⽅法中。
Thread类run⽅法中的任务并不是我们所需要的,只有重写这个run⽅法。既然Thread类已经定义了线程任务的编写位置(run⽅法),那么只要在编写位置(run⽅法)中定义任务代码即可。所以进⾏了重写run⽅法动作。
1.2.2 获取线程名称
开启的线程都会有⾃⼰的独⽴运⾏栈内存,按照⾯向对象的特点,对象的属性和功能,只需对象就可以了。查阅Thread类的API⽂档发现有个⽅法是获取当前正在运⾏的线程对象。还有个⽅法是获取当前线程对象的名称。
java线程池创建的四种Thread.currentThread()获取当前线程对象
Thread.currentThread().getName();获取当前线程对象的名称
1.3 创建线程⽅式—实现Runnable接⼝
创建线程的另⼀种⽅法是声明实现 Runnable 接⼝的类。该类然后实现 run ⽅法。然后创建Runnable的⼦类对象,传⼊到某个线程的构造⽅法中,开启线程。
为何要实现Runnable接⼝,Runable是啥玩意呢?继续API搜索。
查看Runnable接⼝说明⽂档:Runnable接⼝⽤来指定每个线程要执⾏的任务。包含了⼀个 run 的⽆参数抽象⽅法,需要由接⼝实现类重写该⽅法。
接⼝中的⽅法
Thread类构造⽅法
创建线程的步骤。
1、定义类实现Runnable接⼝。
2、覆盖接⼝中的run⽅法。。
3、创建Thread类的对象
4、将Runnable接⼝的⼦类对象作为参数传递给Thread类的构造函数。
5、调⽤Thread类的start⽅法开启线程。
测试类:
1/*
2 * 实现接⼝⽅式的线程
3 * 创建Thread类对象,构造⽅法中,传递Runnable接⼝实现类
4 * 调⽤Thread类⽅法start()
5*/
6public class ThreadDemo {
7public static void main(String[] args) {
8 SubRunnable sr = new SubRunnable();
9 Thread t = new Thread(sr);
10 t.start();
11for(int i = 0 ; i < 50; i++){
12 System.out.println(""+i);
13 }
14 }
15 }
定义Runnable接⼝的实现类
1/*
2 * 实现线程成功的另⼀个⽅式,接⼝实现
3 * 实现接⼝Runnable,重写run⽅法
4*/
5public class SubRunnable implements Runnable{
6//重写run⽅法
7public void run(){
8for(int i = 0 ; i < 50; i++){
9 System.out.println(""+i);
10 }
11 }
12 }
1.3.1 实现Runnable的原理
为什么需要定⼀个类去实现Runnable接⼝呢?继承Thread类和实现Runnable接⼝有啥区别呢?
实现Runnable接⼝,避免了继承Thread类的单继承局限性。覆盖Runnable接⼝中的run⽅法,将线程任务代码定义到run⽅法中。
创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接⼝的run⽅法中,⽽这个run⽅法所属于Runnable接⼝的⼦类对象,所以将这个⼦类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运⾏的线程的任务。
1.3.2 实现Runnable的好处
第⼆种⽅式实现Runnable接⼝避免了单继承的局限性,所以较为常⽤。实现Runnable接⼝的⽅式,更加的符合⾯向对象,线程分为两部分,⼀部分线程对象,⼀部分线程任务。继承Thread类,线程对象和线程任务耦合在⼀起。⼀旦创建Thread类的⼦类对象,既是线程对象,有⼜有线程任务。实现runnable接⼝,将线程任务单独分离出来封装成对象,类型就是Runnable接⼝类型。Runnable接⼝对线程对象和线程任务进⾏解耦。
1.4 线程的匿名内部类使⽤
使⽤线程的内匿名内部类⽅式,可以⽅便的实现每个线程执⾏不同的线程任务操作。
⽅式1:创建线程对象时,直接重写Thread类中的run⽅法
new Thread() {
public void run() {
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName()
+ "...X...." + x);
}
}
}.start();
⽅式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.1 线程池概念
线程池,其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线程对象的操作,⽆需反复创建线程⽽消耗过多资源。
我们详细的解释⼀下为什么要使⽤线程池?
在java中,如果每个请求到达就创建⼀个新线程,开销是相当⼤的。在实际使⽤中,创建和销毁线程花费的时间和消耗的系统资源都相当
⼤,甚⾄可能要⽐在处理实际的⽤户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在⼀个jvm⾥创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”⽽导致系统资源不⾜。为了防⽌资源不⾜,需要采取⼀些办法来限制任何给定时刻处理的请求数⽬,尽可能减少创建和销毁线程的次数,特别是⼀些资源耗费⽐较⼤的线程的创建和销毁,尽量利⽤已有对象来进⾏服务。
线程池主要⽤来解决线程⽣命周期开销问题和资源不⾜问题。通过对多个任务重复使⽤线程,线程创建的开销就被分摊到了多个任务上了,⽽且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以⽴即为请求服务,使⽤应⽤程序响应更快。另外,通过适当的调整线程中的线程数⽬可以防⽌出现资源不⾜的情况。
2.2 使⽤线程池⽅式--Runnable接⼝
通常,线程池都是通过线程池⼯⼚创建,再调⽤线程池中的⽅法获取线程,再通过线程去执⾏任务⽅法。
Executors:线程池创建⼯⼚类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
ExecutorService:线程池类
Future<?> submit(Runnable task):获取线程池中的某⼀个线程对象,并执⾏
Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤
使⽤线程池中线程对象的步骤:
1. 创建线程池对象
2. 创建Runnable接⼝⼦类对象
3. 提交Runnable接⼝⼦类对象
4. 关闭线程池
测试类
1import urrent.ExecutorService;
2import urrent.Executors;
3
4/*
5 * JDK1.5新特性,实现线程池程序
6 * 使⽤⼯⼚类 Executors中的静态⽅法创建线程对象,指定线程的个数
7 * static ExecutorService newFixedThreadPool(int 个数) 返回线程池对象
8 * 返回的是ExecutorService接⼝的实现类 (线程池对象)
9 *
10 * 接⼝实现类对象,调⽤⽅法submit (Ruunable r) 提交线程执⾏任务
11 *
12*/
13public class ThreadPoolDemo {
14public static void main(String[] args) {
15//调⽤⼯⼚类的静态⽅法,创建线程池对象
16//返回线程池对象,是返回的接⼝
17 ExecutorService es = wFixedThreadPool(2);
18//调⽤接⼝实现类对象es中的⽅法submit提交线程任务
19//将Runnable接⼝实现类对象,传递
20 es.submit(new ThreadPoolRunnable());
21 es.submit(new ThreadPoolRunnable());
22 es.submit(new ThreadPoolRunnable());
23
24 }
25 }
Runnable的实现类
1public class ThreadPoolRunnable implements Runnable {
2public void run(){
3 System.out.println(Thread.currentThread().getName()+" 线程提交任务");
4 }
5 }
2.3 使⽤线程池⽅式—Callable接⼝
Callable接⼝:与Runnable接⼝功能相似,⽤来指定线程的任务。其中的call()⽅法,⽤来返回线程任务执⾏完毕后的结果,call⽅法可抛出异常。
ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某⼀个线程对象,并执⾏线程中的call()⽅法
Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤使⽤线程池中线程对象的步骤:
1. 创建线程池对象
2. 创建Callable接⼝⼦类对象
3. 提交Callable接⼝⼦类对象
4. 关闭线程池
测试类
1import urrent.ExecutorService;
2import urrent.Executors;
3import urrent.Future;
4
5/*
6 * 实现线程程序的第三个⽅式,实现Callable接⼝⽅式
7 * 实现步骤
8 * ⼯⼚类 Executors静态⽅法newFixedThreadPool⽅法,创建线程池对象
9 * 线程池对象ExecutorService接⼝实现类,调⽤⽅法submit提交线程任务
10 * submit(Callable c)
11*/
12public class ThreadPoolDemo1 {
13public static void main(String[] args)throws Exception {
14 ExecutorService es = wFixedThreadPool(2);
15//提交线程任务的⽅法submit⽅法返回 Future接⼝的实现类
16 Future<String> f = es.submit(new ThreadPoolCallable());
17 String s = f.get();
18 System.out.println(s);
19 }
20 }
实现Callable接⼝
1/*
2 * Callable 接⼝的实现类,作为线程提交任务出现
3 * 使⽤⽅法返回值
4*/
5
6import urrent.Callable;
7
8public class ThreadPoolCallable implements Callable<String>{
9public String call(){
10return "abc";
11 }
12 }
总结
创建线程的⽅式
⽅式1,继承Thread线程类
步骤
1. ⾃定义类继承Thread类
2. 在⾃定义类中重写Thread类的run⽅法
3. 创建⾃定义类对象(线程对象)
4. 调⽤start⽅法,启动线程,通过JVM,调⽤线程中的run⽅法
⽅式2,实现Runnable接⼝
步骤
1. 创建线程任务类实现Runnable接⼝
2. 在线程任务类中重写接⼝中的run⽅法
3. 创建线程任务类对象
4. 创建线程对象,把线程任务类对象作为Thread类构造⽅法的参数使⽤
5. 调⽤start⽅法,启动线程,通过JVM,调⽤线程任务类中的run⽅法
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论