【Java学习笔记之三十四】超详解Java多线程基础

 

前言

多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧。

正文

线程与进程

1 线程:进程中负责程序执行的执行单元
线程本身依靠程序进行运行
线程是程序中的顺序控制流,只能使用分配给程序的资源和环境

2 进程:执行中的程序
一个进程至少包含一个线程

3 单线程:程序中只存在一个线程,实际上主方法就是一个主线程

4 多线程:在一个程序中运行多个任务
目的是更好地使用CPU资源

线程的实现

继承Thread类

java.lang包中定义, 继承Thread类必须重写run()方法

复制代码
 1 class MyThread extends Thread{  2 private static int num = 0;  3  4 public MyThread(){  5 num++;  6  }  7  8  @Override  9 public void run() { 10 System.out.println("主动创建的第"+num+"个线程"); 11  } 12 }
复制代码

创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。注意,不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

 

复制代码
 1 public class Test {  2 public static void main(String[] args) {  3 MyThread thread = new MyThread();  4  thread.start();  5  }  6 }  7 class MyThread extends Thread{  8 private static int num = 0;  9 public MyThread(){ 10 num++; 11  } 12  @Override 13 public void run() { 14 System.out.println("主动创建的第"+num+"个线程"); 15  } 16 }
复制代码

 

在上面代码中,通过调用start()方法,就会创建一个新的线程了。为了分清start()方法调用和run()方法调用的区别,请看下面一个例子:

 

复制代码
 1 public class Test {  2 public static void main(String[] args) {  3 System.out.println("主线程ID:"+Thread.currentThread().getId());  4 MyThread thread1 = new MyThread("thread1");  5  thread1.start();  6 MyThread thread2 = new MyThread("thread2");  7  thread2.run();  8  }  9 } 10 11 class MyThread extends Thread{ 12 private String name; 13 14 public MyThread(String name){ 15 this.name = name; 16  } 17 18  @Override 19 public void run() { 20 System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId()); 21  } 22 }
复制代码

 

运行结果:

从输出结果可以得出以下结论:

1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;

2)虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

实现Runnable接口

在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。
下面是一个例子:

复制代码
 1 public class Test {  2 public static void main(String[] args) {  3 System.out.println("主线程ID:"+Thread.currentThread().getId());  4 MyRunnable runnable = new MyRunnable();  5 Thread thread = new Thread(runnable);  6  thread.start();  7  }  8 }  9 class MyRunnable implements Runnable{ 10 public MyRunnable() { 11  } 12 13  @Override 14 public void run() { 15 System.out.println("子线程ID:"+Thread.currentThread().getId()); 16  } 17 }
复制代码

Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。

事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。

在Java中,这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。

使用ExecutorService、Callable、Future实现有返回结果的多线程

多线程后续会学到,这里暂时先知道一下有这种方法即可。

ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。返回结果的线程是在JDK1.5中引入的新特征,确实很实用,有了这种特征我就不需要再为了得到返回值而大费周折了,而且即便实现了也可能漏洞百出。

可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:

复制代码
 1 /**
 2 * 有返回值的线程  3 */  4 @SuppressWarnings("unchecked")  5 public class Test {  6 public static void main(String[] args) throws ExecutionException,  7  InterruptedException {  8 System.out.println("----程序开始运行----");  9 Date date1 = new Date(); 10 11 int taskSize = 5; 12 // 创建一个线程池  13 ExecutorService pool = Executors.newFixedThreadPool(taskSize); 14 // 创建多个有返回值的任务  15 List<Future> list = new ArrayList<Future>(); 16 for (int i = 0; i < taskSize; i++) { 17 Callable c = new MyCallable(i + " "); 18 // 执行任务并获取Future对象  19 Future f = pool.submit(c); 20 // System.out.println(">>>" + f.get().toString());  21  list.add(f); 22  } 23 // 关闭线程池  24  pool.shutdown(); 25 26 // 获取所有并发任务的运行结果  27 for (Future f : list) { 28 // 从Future对象上获取任务的返回值,并输出到控制台  

转载于:https://www.cnblogs.com/DWVictor/p/10507779.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值