java学习总结(四)

来源:互联网 发布:网络零售的理解 编辑:程序博客网 时间:2024/06/05 06:30

java学习总结(四)

——关于线程与锁

Java的内存模型

之前在回答过程中一直会把JAVA的内存模型和JVM的内存模型弄混淆,那么在这里一定需要注意。

Java的内存模型由以下三个部分组成:主内存(进程分配的内存)、工作内存、Java线程。

1、不同线程之间没有办法访问其工作内存的变量。
2、线程之间变量传递主要通过主内存来完成。
JAVA内存模型

进程与线程:

程序的一次执行称作一个进程,而线程,是操作系统调动的基本单位。
1、多进程有利于整个程序的健壮性,但是系统资源开销较大,相比之下多线程编写的程序利用并发特点,能很好利用CPU,提高效率。
2、进程有其单独的内存空间,而各个线程共享同一个主进程的内存空间。因此对于共享变量就存在同步问题需要解决。

几种进程间的通信方式

(1) 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。
(2)有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
(3)信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
(4)消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(5)信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。
(6)共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
(7)套接字(socket):套接口也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同及间的进程通信。

线程之间的通信

在操作系统中,线程的通信主要分为两种:1、共享内存。2、消息传递
其次就是一些同步原语,这里列举一些python的同步原语,Java也同理:
锁(lock/release):这两个原语其中框住的部分和Java中synonymous关键字修饰的同步代码块相同。
信号量(signal):用于描述竞争的资源。共享变量(资源)消耗时递减,释放时增加。生产者——消费者模型就是一个很好的例子。
重入锁(relock):多次调用acquire()而不会被阻塞,可多次申明对资源的需求。锁内部有计数器,只有release()和acquire()方法调用次数相同时,才会真正解锁。
条件变量(condition):是对于锁的封装,和信号量相似,也用于调用竞争资源。当资源状态不可用时,线程会自动释放手中的锁,当生产者产生物品时候,这时线程又会立马被唤醒。
事件(event):
计时器(timer):可设置等待启动的时间,在启动前可调用cancel()方法取消,相当于睡眠。

Java的线程安全

线程安全的实现方法:
1、互斥同步:参见同步原语
2、非阻塞同步:先不加锁,数据操作冲突时候再进行补偿操作。
3、无同步方案:1、可重入代码。2、线程本地存储(ThreadLocal):如果一段代码所需要的数据必须与其它代码共享,可以看看这些共享数据代码是否能保证再同一个线程中执行。

我们衡量线程安全一定不要受到时间顺序的干扰,要以先行发生原则为准

Java的线程的实现是通过用户线程(系统感觉不到线程存在的实现,缺点是完全依赖用户编程实现细节,任务量大)以及轻量级线程混合实现的。

Java线程的调度由两种:
1、协同式线程调度:线程之间进行资源的商议。缺点是线程的执行时间得不到有效控制。优点是实现简单。
2、抢占式线程调度:虽然可以设置线程的优先级,但是,线程的调度的最终状态还是取决于操作系统。

守护线程

volatile关键字

volatile可以说是Java提供的最轻量级的同步机制。
1、volatile保证变量的可见性。而可见性只是说每次对变量修改完成以后都同步回主存。但并不保证变量的原子性。即一次只有一个线程对变量进行操作。所以其修饰的共享变量在多线程环境中一样是不安全的
2、禁止指令重排优化。通过内存屏障机制,即在执行命令后,立马将数据store and write同步到主内存。

锁优化

重量级锁
synchronized()
轻量级锁
偏向锁
JDK1.6引入,锁偏向于第一个获得它的线程。

上述两种情况都是在假设不存在竞争的情况下,尝试对共享数据进行操作,如果存在竞争或膨胀为重量级锁,主要提高的是有同步但是无竞争的程序性能。
自旋锁
自旋即进入忙循环。JDK1.6中引入了自适应自旋锁。避免挂起线程与恢复线程带来的操作系统的损耗。
锁粗化
对同一个对象加锁,会把加锁的同步范围扩展(粗化)到整个操作序列的外部。
锁消除
被检测到不可能存在共享数据竞争的锁进行消除。特例:字符串相加。

Java线程状态:

就绪(new)  运行(runable) 阻塞(blocked) 等待(waiting) 结束(terminate) 

这其中主要要弄清楚阻塞与等待之间的区别,阻塞要等待线程放弃锁,而等待,则是等待特定的之间或者是被其他线程所唤醒。