java随笔-多线程(1)

本篇博客是对本人学习多线程的知识点的总结,这是第一部分.

1.多线程的意义

相对于传统的单线程,多线程能够在操作系统多核配置的基础上,能够更好地利用服务器的多个CPU资源,使得程序运行起来更加高效.Java通过提供对多线程的支持,在一个进程内并发执行多个线程,每个线程都并行执行不同的任务,以满足高并发编程的要求.
多线程能够实现多个内存去和cpu交换信息,相当于几个人去做一件事,人多力量大,可以节省时间,同时也不浪费计算机的性能.

2.多线程的实现

常用的实现方式有三种:
第一,javaSE中存在一个Thread类,这个类用来封装多线程操作的各种方法.当我们需要一个或多个线程去完成同一的任务的时候,我们将这些线程以类为单位同时继承Thread类,并重写父类中的run()方法,run()方法表示这个线程要做什么.然后实例化这些线程对象,并分别调用start()方法.start()方法是一个native方法,通过在操作系统上启动一个新线程,并最终执行run方法来启动过一个线程.run方法内的代码是线程类的具体实现逻辑.
简单来说如下:

1.定义若干个线程类,使它们继承Thread类,并重写run方法
2.在主方法中实例化这些类,并分别调用start方法以启动线程.

第二,鉴于第一种实现方式是通过继承Thread类,而继承是单向的.对于一些已经继承过其他类的子类而言,它就不能再继承Thread类了,此时可以通过实现Runnable接口来创建线程.具体实现过程如下:

1.让线程类去实现Runnable接口,并重写run方法,这里是必须要重写的.
比如:ChildrenThread emplements Runnable{
	@Override
	run(){
	...
	}
}
2.令这些线程类实例化.比如:
ChildrenThread1 ct1 = new ChildrenThread();
ChildrenThread ct2 = new ChildrenThread();
3.实例化一个Thread类,将这些实现类作为参数传入.比如:
Thread t1 = new Thread(ct1);
Thread t2 = new Thread(ct2);
4.分别调用start方法.
t1.start();
t2.start();

要注意,在调用start()方法的时候,事实上调用的是子线程的run()方法而不是Thread示例的run()方法.
第三,在我们需要在主线程中开启多个子线程并发执行一个任务,并且收集各个线程执行返回的结果并将最终的结果汇总起来,这时就要用到Callable接口.具体的实现方法为:创建一个类并实现Callable接口,在call方法中实现具体的运算逻辑并返回计算结果.具体的调用过程为:创建一个线程池,一个用于接收返回结果的Futuer List及Callable线程实例,使用线程池提交任务并将线程执行之后的结果保存在Future中,在线程执行结束后遍历Future List中的Future对象,在该对象上调用get方法就可以获取Callable线程任务返回的数据并汇总结果.
具体步骤如下:

1.声明一个线程类并实现Callable接口
class MyCallable implements Callable{}
2.重写其中的call()方法,call方法里是线程的任务执行逻辑,它是有返回值的.接下来的步骤在main方法完成.
3.每有一个线程,就实例化一个MyCallable类
MyCallable task1 = new MyCallable();
MyCallable task2 = new MyCallable();
...
4.创建一个固定大小的线程池,nThreads表示最大线程个数
ExecutorService pool = Executors.newFixedThreadPool(nThreads);
5.当要启动这些线程的时候,3中实例化的任务对象作为参数传入线程池的submit()方法中,返回对象是Future
Future future1 = pool.submit(task1);
Future future2 = pool.submit(task2);
...
6.当我们需要这些返回值的时候,则使future对象们调用get()方法,get()方法会产生阻塞,直到线程完成任务才会执行,以获取线程执行的结果返回值.
Object value1 = futuer1.get();
Object value2 = futuer2.get();
...

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。它有五个方法,分别是:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

它的作用和功能参考以下博客:
Java多线程Callable接口
线程池的原理将在后面介绍.

3.多线程的生命周期

对于一个线程来说,它具有新建(New),就绪(Runnable),运行(Running),阻塞(Blocked)和死亡(Dead)这五种状态.具体状态的转化流程如图所示:
在这里插入图片描述

4.线程的基本方法

在这里插入图片描述

sleep方法

调用sleep方法会导致当前线程休眠.与wait方法不同的是,sleep方法不会释放当前占有的锁.wait方法是对象具有的方法,它只有在线程进入锁对象以后调用才不会抛出异常.它们之间的联系和区别将在后面讲.另外,sleep是需要捕获异常的,否则无法编译.sleep具有一个形参,用以描述休眠时间,单位是毫秒.当线程开始休眠时,即使它处于while(true)循环中,也会立即停止运行,并抛出java.lang.InterruptedException异常,表示其没有完成任务即中断.

yield方法

调用yield方法会使当前线程让出CPU执行时间片,与其他线程一起重新竞争CPU.

join方法

join方法用于等待其他线程终止,如果在当前线程中调用一个线程的Join方法,则当前线程会转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转化为就绪状态,等待获取CPU的使用权.在很多情况下,主线程生成并启动了子线程,需要等到子线程返回结果并收集和处理再退出,这时就要用到join方法.
所谓主线程,指的就是执行main方法代码块的线程.在没有学到多线程的时候,我们一般用的都是主线程处理任务.

5.终止线程的4种方式

1.正常运行结束

2.使用退出标志退出线程

在一般情况下,在run方法执行完毕后,线程会正常结束.然而,有些线程是后台线程,需要长时间运行,只有在系统满足某些特殊条件后,才能退出这些线程.这时候可以使用一个变量来控制循环,比如:

public class ThreadSafe extends Thread{
	public volatile boolean exit = false;
	public void run(){
		while(!exit){
		//执行业务逻辑代码
		}
	}
}

关键字volatile用于保证exit线程同步安全.也就是说在同一时刻只能有一个线程修改exit的值,在exit为true时,while循环退出,线程终止.

3.使用interrupt方法终止线程

有以下两种情况:
(1)线程处于阻塞状态,在调用interrupt方法时,会抛出InterruptException异常.捕获该异常,然后通过break跳出状态检测循环,结束这个线程的执行
(2)线程处于未阻塞状态,此时使用isInterrupt方法来判断线程的中断标志来退出循环.

4.使用stop方法终止线程:不安全

就像突然关闭计算机的电源,而不是正常关机一样,可能会产生不可预料的结果.,因此不推荐.
暂时就写到这里,如有错误敬请指出.
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值