文档章节

JAVA线程[Thread]--同步篇

Candy_Desire
 Candy_Desire
发布于 2014/11/14 10:26
字数 1765
阅读 929
收藏 4
点赞 2
评论 0

一、线程简介:

线程(Thread)是进程中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。

二、共享资源:

所谓共享资源是指可以被一个以上任务使用的资源叫做共享资源。

为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。而多个线程之间有可能共享一些资源如:内存,文件,数据库等。 当多个线程同时读写同一份共享资源的时,可能会引起冲突。这时候就需要引入线程“同步”机制。 

三、同步机制:

同步(synchronized)实际含义和字面相反。同步是指有序,各线程之间要有序对共享资源进行操作,而不是同时进行操作。 

对于线程同步容易混淆和需要注意的地方:

(1)、同步是指有序的执行共享资源。

(2)、只有共享的资源才需要同步。

(3)、共享的常量资源不需要同步,只有变量资源需要同步。

(4)、无论是否执行同一段代码,只要共享可变资源就需要同步。

四、同步锁:

线程同步的基本实现思路是给共享资源加锁,只有线程获取了这把锁,才有权利访问该共享资源。 

既然是共享资源需要同步,所以同步锁固然是要加在共享资源上,对于一些比较完善的共享资源如:文件系统,数据库系统等,他们自身就提供了比较完善的同步锁机制,我们就不必再添加锁了。

对于同步锁容易混淆和需要注意的地方:

(1)、一般说线程同步的实现方式是给共享资源加锁,这样的表述容易让人误以为锁是要加在共享资源上面的,而实际上同步锁是要加在访问共享资源的代码段上的。

(2)、访问同一份共享资源的不同代码段,应该加同一个锁。如果不是同一个锁就没有同步的意义啦。就是说同步锁本身也是多线程之间的共享对象。

五、实现方式:

同步锁的声明可以不必为static或public,但是一定要保证同步代码之间要使用同一个锁。在Java中任何一个Object Reference都可以作为同步锁,可以理解为对象在内存分配系统中的内存地址,因此要实现同步就必须保证同步锁指向同一个地址。

同一时刻,只有一个线程可以获得lock的所有权,其他竞争lock的线程只能暂停运行,进入到该同步锁的就绪(Ready)队列。一般同步锁都挂有几个线程的队列,包括就绪队列(Ready)、待召队列(Waiting)等。就绪队列里的线程时刻准备的运行,而待召队列里的线程只能一直等待,直到等到某个信号的通知。才能加入到就绪队列中竞争同步锁。成功获得锁的线程执行完代码段后,会自动释放同步锁,就绪队列的线程可继续竞争锁。因此线程同步时非常消耗资源的,要控制好同步粒度。

【注:竞争同步锁失败的线程进入该锁的就绪队列(Ready)不是待召队列(Waiting)】

(1)、正确例子

public static final Object lock =new Object();
synchronized(lock){
//访问共享资源的代码段
}

注:同步锁也要是共享的对象】

(2)、错误例子

void synTest(){
Object lock =new Object();
    synchronized(lock){
    //访问共享资源的代码段
    }
}

【注:由于同步锁是由函数体内部产生的,每个线程调用这段代码都会新建一个同步锁,因此没有同步意义】

六、同步粒度:

synchronized关键字可以直接加在函数的定义上。

void synchronized synTest() { 
//代码段 
}

上面代码等价于

void  synTest() { 
    synchronized(this){
    //对象本身就是锁
    //代码段 
    }
}

//同样适用于静态函数
void  synTest() { 
    synchronized(Class.forName(...)){
    //类本身就是锁
    //代码段 
    }
}

【注:尽量避免把synchronized直接加载函数上,要控制好同步的粒度,同步的代码段越小越好】

在控制同步粒度的同时也要细分好同步锁。例如要同步不同的共享资源时就没有必要公用同一个锁。

例如:

public static final Object lockOne = new Object(); 

void threadOne(){
    synchronized(lockOne){
    //共享资源A
    }
}

void threadTwo(){
    synchronized(lockOne){
    //共享资源B
    }
}

上段代码不是共享一个资源但是都要竞争同一个锁,这样会降低程序的效率,可重新定义个新锁用于B资源的同步。

七、信号量:

信号量模型比同步锁模型复杂许多可跨进程同步,还有计数和控制同事运行线程数等功能,用于处理更复杂的同步模型如:读写同步、生产者、消费者模型等。

信号量的运行方式是可以主动停下正在运行的线程,等待某个信号量的通知后,此时该线程进入到待召队列(Waiting)等到通知后该线程再次进入到就绪队列(Ready)和其他线程竞争同步锁。

信号量的基本模型是等待/通知模型,其他较为复杂的模型都是在此基础上衍生出来的。这里我们先将就基本模型,其他模型后续再更新。

在Java中任何一个Object Reference都可以作为同步锁和信号量。我们知道Object对象有wait()和notify()两个方法,这两个方法分别就是等待通知和发出通知的方法。

(1)、wait的用法:

public static final Object signal = new Object(); 

void waitTest(){
    //获取信号量(同步锁)
    synchronized(signal){
    //该线程进入signal对象的待召队列(Waiting)
    signal.wait();
    //本线程放弃同步锁,后续代码不在执行。
    }
}

【注:signal.wait()不是signal开始等待,而是当前线程开始等待这个signal对象】

wait其实还可以定义等待时间,当时间到了以后处于待召队列中的线程就不在等待直接进入到就绪队列。

(2)、notify的用法:

public static final Object signal = new Object(); 

void notifyTest(){
    //获取信号量(同步锁)
    synchronized(signal){
    //通知signal对象中待召队列中等待的线程进入到就绪队列
    signal.notify();
    //本线程依然持有同步锁,后续代码继续执行。
    }
}

【注:signal.notify()不是通知signal这个对象本身,而是通知正在等待信号量的其他线程进入到就绪队列,线程本身继续持有同步锁,并正常执行后续代码】

还有一个notifyAll()方法,该方法是通知待召队列里面的所有线程。用法类似这里不做介绍了


© 著作权归作者所有

共有 人打赏支持
Candy_Desire
粉丝 31
博文 71
码字总数 84592
作品 0
浦东
产品经理
Java 编程之美:并发编程高级篇之一

本文来自作者 追梦 在 GitChat 上分享 「Java 编程之美:并发编程高级篇之一」 编辑 | 工藤 前言 借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了。 ...

gitchat ⋅ 05/24 ⋅ 0

深入浅出Java多线程,看着篇就够了

Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。 多线程是多任务的一种特别的形...

java高级架构牛人 ⋅ 05/22 ⋅ 0

java基础thread——多线程的纷争(循序渐进)

一、多线程概述 进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。 多进程有什么意义呢? 单进程的计算机只能做一件事情,而我们现在...

潇潇漓燃 ⋅ 05/30 ⋅ 0

ThreadLocal趣谈 —— 杨过和他的四个冤家

一个一个上 一日醒来,杨过发现小龙女离家出走,于是外出寻找,不料碰上了金轮法王、李莫愁、裘千尺、公孙止四个冤家。 “哼,四个打我一个,算什么英雄好汉,有本事的,一个一个上!” 按照...

SexyCode ⋅ 06/12 ⋅ 0

JVM(Thread/Stack)

JVM Thread/Stack Memory Size JVM Thread/Stack Object states (6 states) Dump OS Thread/Stack OS的线程运行状态 Iuput(top): Output: or input(ps): Dump Thread/Stack Analysis 注意thr......

赵-猛 ⋅ 2016/10/12 ⋅ 0

悲观的并发策略——synchronized互斥锁

互斥锁是最常见的同步手段,在并发过程中,当多条线程对同一个共享数据竞争时,它保证共享数据同一时刻只能被一条线程使用,其他线程只有等到锁释放后才能重新进行竞争。 对于Java开发人员,...

wangyangzhizhou ⋅ 04/16 ⋅ 0

ThreadLocal可能引起的内存泄露

  threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好的做法是将调用thr...

天天顺利 ⋅ 06/15 ⋅ 0

[Java 并发编程] 集合框架之 同步容器类 & 并发容器类

吾生也有涯,而知也无涯。———《庄子》 通过上一篇文章,我们已经知道设计一个线程安全类的原则和步骤,以及在设计过程中我们应当注意的细节。实际上,Java 的集合库包含了线程安全集合和非...

seaicelin ⋅ 05/25 ⋅ 0

java面试必备之ThreadLocal

按照传统的经验,如果某个对象是非线程安全的,在多线程环境下对象的访问需要采用synchronized进行同步。但是模板类并未采用线程同步机制,因为线程同步会降低系统的并发性能,此外代码同步解...

编程老司机 ⋅ 05/16 ⋅ 0

深入学习Lock锁(2)——LockSupport工具类

在同步组件中,当需要阻塞或唤醒一个线程的时候,都会使用LockSupport工具类来完成相应 工作。LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSup...

江左煤郎 ⋅ 05/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

BS与CS的联系与区别【简】

C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或 SQL Server。客户端需要安装专用的客户端软件。 B/S是Brower/...

anlve ⋅ 39分钟前 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 59分钟前 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部