java 工作流多线程_java多线程

1、多线程的意义

操作系统可以多任务执行,每个任务就是就是一个进程。

每个任务(进程)可以分多工作流分别执行。

比较:进程:有独立的代码和数据空间(进程上下文),进程切换开销大,进程是资源分配的最小单位。

线程:每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。线程是cpu调度的最小单位。

并行与并发:

并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。

进程和线程的生命周期:创建、就绪、运行、阻塞、终止

de326606b8f084fd9854ef4c8cc86d32.png

1)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用

2) sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。

3)wait、sleep和join都可以用interrupt()打断,如果此刻线程B正在wait/sleep /join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。

2、java多线程的实现

Java线程的实现是基于一对一的线程模型,所谓的一对一模型,实际上就是通过语言级别层面程序去间接调用系统内核的线程模型,即我们在使用Java线程时,Java虚拟机内部是转而调用当前操作系统的内核线程来完成当前任务。

由于我们编写的多线程程序属于语言层面的,程序一般不会直接去调用内核线程,取而代之的是一种轻量级的进程(Light Weight Process),也是通常意义上的线程,由于每个轻量级进程都会映射到一个内核线程,因此我们可以通过轻量级进程调用内核线程,进而由操作系统内核将任务映射到各个处理器,这种轻量级进程与内核线程间1对1的关系就称为一对一的线程模型。

5af4637711de1e321c8773d36f20a0dc.png

每个线程最终都会映射到CPU中进行处理,如果CPU存在多核,那么一个CPU将可以并行执行多个线程任务。

创建多线程的方式:

1)继承Thread类:new myThread(“A线程”).start();

2)实现Runnable接口:new Thread(new myThread("A线程")).start();

3)实现Callable接口

3、java多线程的内存模型

63c4c75fe86f10fb61008a25f8b1c22f.png

前面说了,线程是cpu的最新调度单位,即cup在执行一个任务(进程)时,是执行的进程中的更小粒度线程。

执行方式:1)(创建)线程对象。即为该线程分配内存并初始化,其中一个工作就是拷贝主存中的共享变量到线程工作内存(拷贝时:对于基本类型,直接拷贝值;对于引用类型,拷贝引用变量,其值仍保存堆中);

2)线程调用start()方法(就绪)。

3)线程获取到cpu时间片后,恢复cpu(运行)现场。即将线程的相关数据写入cpu缓存,再到cpu寄存器等进行执行。

4)(结束)将在cpu中处理后的数据刷新到主存。然后空出的cpu继续加载其他线程的数据进行执行。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public class MyThread extendsThread{int a=1;//多线程中的共享数据(基本数据类型),运行时直接拷贝值到线程工作内存

String str;//多线程中的共享数据(引用类型),运行时拷贝引用变量到线程工作内存

MyThrad(String str){this.str =str;}

@Overridepublic voidrun(){

str=str+a;

}public static voidmain(String[] args){

String str=new String("hgp");//启动线程后会拷贝共享变量到各自线程的工作内存,处理完了再分别刷新到主存(就是上面堆内存中的str对象中)

newMythread(str).start();newMythread(str).start();

}

}

View Code

4、线程同步

不管是什么内存模型,最终还是运行在计算机硬件上的。当一个CPU需要访问主存时,会先读取一部分主存数据到CPU缓存,进而在读取CPU缓存到寄存器。当CPU需要写数据到主存时,同样会先flush寄存器到CPU缓存,然后再在某些节点把缓存数据flush到主存。

3fd90b467ec969b408ee4e5cd667e126.png

前提:上面说了对于线程的执行,并不是直接进行的,主存共享变量的操作要经过读入线程内存、cpu缓存等,待数据在cpu中执行完了再经过缓存、线程内存写入主存。也就是说线程的执行有一个时间过程,在这个过程中多线程对同一代码块的执行是彼此独立的,因为cpu操作的操作数已经是主存数据的拷贝了。

1)这样的话若是非原子性操作就易出现错误,解决方法——线程同步,通过对象锁机制实现,用关键字synchronized声明。

synchronized关键字的锁机制,锁的是对象:

当synchronized使用在方法上或成员变量上,锁的是该实例对象;

当synchronized使用在static静态成员上,锁的是该类型的Class对象。

当synchronized使用在自定义代码块上,可以自定义执行该代码块需要的锁对象。

注:synchronized锁的是对象,不同对象锁不同,如当访问一个同步方法时,该对象的其他同步方法也被锁;普通同步方法与静态同步方法互不干扰。还有synchronized关键字不能被继承。

2)在线程把操作的共享数据刷新到主存之前,对共享数据的更改对其他线程来说是不可见的,解决方法——volatile 关键字,保证变量会直接从主存读取,而对变量的更新也会直接写到主存。

voilatile只是解决可见性,对于非原子操作并不保证线程安全。

总结:原子性——原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。

可见性——指的是当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值。

重排序——计算机在执行程序时,为了提高性能,编译器和处理器的常常会对指令做重排。volatile会禁用重排序。

有序性——指的是单线程内保证串行语义执行的一致性,后半句则指指令重排现象和工作内存与主内存同步延迟现象。

参考:http://blog.csdn.net/suifeng3051/article/details/52611310

http://blog.csdn.net/javazejian/article/details/72772461

https://www.cnblogs.com/wxd0108/p/5479442.html

https://www.cnblogs.com/GarfieldEr007/p/5746362.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值