当前位置:首页 » 操作系统 » java并发源码

java并发源码

发布时间: 2022-08-07 11:58:46

① 什么是java多线程编程

一、 什么是多线程:

我们现在所使用操作系统都是多任务操作系统(早期使用的DOS操作系统为单任务操作系统),多任务操作指在同一时刻可以同时做多件事(可以同时执行多个程序)。

  • 多进程:每个程序都是一个进程,在操作系统中可以同时执行多个程序,多进程的目的是为了有效的使用CPU资源,每开一个进程系统要为该进程分配相关的系统资源(内存资源)

  • 多线程:线程是进程内部比进程更小的执行单元(执行流|程序片段),每个线程完成一个任务,每个进程内部包含了多个线程每个线程做自己的事情,在进程中的所有线程共享该进程的资源;

  • 主线程:在进程中至少存在一个主线程,其他子线程都由主线程开启,主线程不一定在其他线程结束后结束,有可能在其他线程结束前结束。Java中的主线程是main线程,是Java的main函数;

  • 二、 Java中实现多线程的方式:

  • 继承Thread类来实现多线程:

  • 当我们自定义的类继承Thread类后,该类就为一个线程类,该类为一个独立的执行单元,线程代码必须编写在run()方法中,run方法是由Thread类定义,我们自己写的线程类必须重写run方法。

    run方法中定义的代码为线程代码,但run方法不能直接调用,如果直接调用并没有开启新的线程而是将run方法交给调用的线程执行

    要开启新的线程需要调用Thread类的start()方法,该方法自动开启一个新的线程并自动执行run方法中的内容

    java多线程的启动顺序不一定是线程执行的顺序,各个线程之间是抢占CPU资源执行的,所有有可能出现与启动顺序不一致的情况。

    CPU的调用策略:

    如何使用CPU资源是由操作系统来决定的,但操作系统只能决定CPU的使用策略不能控制实际获得CPU执行权的程序。

    线程执行有两种方式:

    1.抢占式:

    目前PC机中使用最多的一种方式,线程抢占CPU的执行权,当一个线程抢到CPU的资源后并不是一直执行到此线程执行结束,而是执行一个时间片后让出CPU资源,此时同其他线程再次抢占CPU资源获得执行权。

    2.轮循式;

    每个线程执行固定的时间片后让出CPU资源,以此循环执行每个线程执行相同的时间片后让出CPU资源交给下一个线程执行。

    希望对您有所帮助!~

② java需不需要培训需要学到什么程度

JAVA自学难度比较大,JAVA编程虽然零基础也可以学,但是更适合有专业基础、逻辑思维能力强、有耐力的人去学习,要求对编程充满热情,最好是有一定编程基础的人,更容易入门。

中小型公司
说到中小型公司,泛指那些100人以上,500人以下的公司,是还算有所发展的公司,这类公司稍微会正规一点,招聘和培训可能会有自己的一套标准,比如学历上可能稍微做一些要求,技术上的把关更严格一点,除了Java基础知识和项目经历之外,可能还会考查你的debug能力,代码规范、异常处理能力,以及对一些Java高级特性的理解能力,可能最好多用过一些框架。
总而言之,这类公司选人的标准更加有体系,不会像一些特别小的公司,招人很随意,领导拍个板就行。

二三线互联网公司
这类公司范围就很广了,比如日薄西山的搜狐、新浪、360,或者迅雷、金山、猎豹,甚至是爱奇艺、携程这类现状比较不错的企业等等,这类公司挤不到BAT、TMD等一线互联网行列,但是在二三线阵容还算是比较不错的公司,它们对于人才的要求其实还是相对比较高的。
比如一般都会要求本科学历,对Java基础知识要比较熟悉,最好能够看过源码,如果没看过,那么源码方面的面试题好歹也要准备一下,除此之外,一般来说还会考察你的后端技术知识,比如数据库、网络、操作系统,考察的不会太难,能把面经上的知识点掌握了就算是比较扎实了。
这类公司一般不会考太复杂的题目,更希望招一些水平能力都是中等的人才,只要知识面能比较广,题目都能说到点子上,不需要掌握得特别深入,也可以有机会拿到offer。
一线互联网公司和独角兽
BAT、TMD等互联网名企都属于这类公司,这类公司和二三线互联网公司的发展差距还是比较大的,体现在公司的规模、市值、甚至是股价等方面,业务以技术为基础,因此这些公司的技术往往也是业界最顶尖的,比如阿里的云计算和中间件,头条的推荐算法、腾讯的游戏技术等等。

要进这些公司,不仅要做到之前那些事情:掌握Java基础、计算机基础知识,并且是非常熟练地掌握,你需要深入理解每一个知识点,因为面试官会不断深入地向你提问,了解你的知识深度,同时,你需要对源码有所理解,在读懂源码的基础上去理解框架的实现、JDK的实现。
另外,你需要对JVM有一个清晰的认识,不仅要了解其结构,垃圾回收原理,甚至还要知道如何在遇到线上问题时通过JVM调优来解决它们。
同理,你还需要对Java并发编程和网络编程的使用方法与底层实现原理非常熟悉,不仅仅答出NIO和BIO的区别,或者是synchronized和lock的区别,你还需要知道NIO的底层实现epoll是什么,synchronized对应的mutex lock是什么,lock和condition的实现原理又是什么,而lock本身也是通过AQS、CAS操作类等组件来实现的,其中的内容实在太多,绝不只是几道面试题就可以搞定的。
当然,除此之外,这些公司对数据库、缓存、分布式技术等方面的要求都会比其他公司要高得多,你最好要搞懂Mysql存储引擎、索引和锁的实现原理,Redis缓存的数据结构、备份方式、底层实现。同时如果你能理解负载均衡算法、CAP理论,甚至是raft和paxos算法,以及分布式常用技术如消息队列、zookeeper等等,那么无疑也是可以为你加分的技能。
为什么大公司的要求这么高,因为它们是最好的互联网公司,要招的自然也是最优秀的人才,如果考察底层原理还不能满足他们筛选人才的需要,他们也会考察面试者的算法能力,比如LeetCode上medium难度的原题,或者是剑指offer的变式题等等,算法题相对考察理论基础而言,筛选度更高,可以淘汰的人也更多。

③ 《Java并发编程的艺术》epub下载在线阅读,求百度网盘云资源

《Java并发编程的艺术》(方腾飞)电子书网盘下载免费在线阅读

资源链接:

链接:

提取码: 37t6

书名:Java并发编程的艺术

作者:方腾飞

豆瓣评分:7.4

出版社:机械工业出版社

出版年份:2015-7-1

页数:240

内容简介:

并发编程领域的扛鼎之作,作者是阿里和1号店的资深Java技术专家,对并发编程有非常深入的研究,《Java并发编程的艺术》是他们多年一线开发经验的结晶。本书的部分内容在出版早期发表在Java并发编程网和InfoQ等技术社区,得到了非常高的评价。它选取了Java并发编程中最核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理和方法,对Java并发编程进行了最为深入和透彻的阐述。

《Java并发编程的艺术》内容涵盖Java并发编程机制的底层实现原理、Java内存模型、Java并发编程基础、Java中的锁、并发容器和框架、原子类、并发工具类、线程池、Executor框架等主题,每个主题都做了深入的讲解,同时通过实例介绍了如何应用这些技术。

作者简介:

方腾飞(花名清英,英文名kiral),

蚂蚁金服集团技术专家,从事Java开发近10年。5年以上的团队管理、项目管理和敏捷开发经验,崇尚团队合作。曾参与CMS、电子海图、SOC、ITIL、电子商务网站和信贷管理系统等项目。目前在蚂蚁金服网商银行贷款管理团队负责数据采集平台开发工作。与同事合作开发了tala code Review插件,深受阿里数千名工程师拥趸,并开发过开源工具jdbcutil(https://github.com/kiral/utils)。创办了并发编程网,组织翻译了百余篇国外优秀技术文章,并曾为InfoQ撰写“聊聊并发”专栏,在《程序员》杂志撰写敏捷实践系列文章

魏鹏,

阿里巴巴集团技术专家,在阿里巴巴中国网站技术部工作多年,曾担任中国网站交易平台架构师,主导了交易系统服务化工作,设计实现的数据迁移系统高效地完成了阿里巴巴中国网站交易数据到阿里巴巴集团的迁移工作。目前在阿里巴巴共享业务事业部从事Java应用容器Pandora和服务框架HSF的相关工作,其中Java应用容器Pandora是阿里巴巴中间件运行的基础,而服务框架HSF则是阿里巴巴集团实现服务化的主要解决方案,二者在阿里巴巴拥有最为广泛的使用量。个人平时喜欢阅读技术书籍,翻译一些国外优秀文档,喜欢总结、乐于分享,对Java应用容器、多线程编程以及分布式系统感兴趣。

程晓明,

1号店资深架构师,从事1号店交易平台系统的开发,技术上关注并发与NIO。因5年前遇到的一个线上故障,解决过程中对Java并发编程产生了浓厚的兴趣,从此开始了漫长的探索之旅:从底层实现机制、内存模型到Java同步。纵观我自己对Java并发的学习过程,是一个从高层到底层再到高层的一个反复迭代的过程,我估计很多读者的学习过程应该与我类似。文章多见诸《IBM developerWorks》、InfoQ和《程序员》杂志。

④ Java多线程是什么意思

Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

1、继承Thread类实现多线程
继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

代码说明:
上述代码中Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
public static ScheledExecutorService newScheledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

总结:ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

⑤ 100万并发连接服务器笔记之Java Netty处理1M连接会怎么样

每一种该语言在某些极限情况下的表现一般都不太一样,那么我常用的Java语言,在达到100万个并发连接情况下,会怎么样呢,有些好奇,更有些期盼。
这次使用经常使用的顺手的netty NIO框架(netty-3.6.5.Final),封装的很好,接口很全面,就像它现在的域名 netty.io,专注于网络IO。
整个过程没有什么技术含量,浅显分析过就更显得有些枯燥无聊,准备好,硬着头皮吧。
测试服务器配置
运行在VMWare Workstation 9中,64位Centos 6.2系统,分配14.9G内存左右,4核。
已安装有Java7版本:
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

在/etc/sysctl.conf中添加如下配置:
fs.file-max = 1048576
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

在/etc/security/limits.conf中添加如下配置:
* soft nofile 1048576
* hard nofile 1048576

测试端
测试端无论是配置还是程序和以前一样,翻看前几篇博客就可以看到client5.c的源码,以及相关的配置信息等。
服务器程序
这次也是很简单呐,没有业务功能,客户端HTTP请求,服务端输出chunked编码内容。
入口HttpChunkedServer.java:
唯一的自定义处理器HttpChunkedServerHandler.java:
启动脚本start.sh
达到100万并发连接时的一些信息
每次服务器端达到一百万个并发持久连接之后,然后关掉测试端程序,断开所有的连接,等到服务器端日志输出在线用户为0时,再次重复以上步骤。在这反反复复的情况下,观察内存等信息的一些情况。以某次断开所有测试端为例后,当前系统占用为(设置为list_free_1):
total used free shared buffers cached
Mem: 15189 7736 7453 0 18 120
-/+ buffers/cache: 7597 7592
Swap: 4095 948 3147

通过top观察,其进程相关信息
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4925 root 20 0 8206m 4.3g 2776 S 0.3 28.8 50:18.66 java

在启动脚本start.sh中,我们设置堆内存为6G。
ps aux|grep java命令获得信息:
root 4925 38.0 28.8 8403444 4484764 ? Sl 15:26 50:18 java -server...HttpChunkedServer 8000

RSS占用内存为4484764K/1024K=4379M
然后再次启动测试端,在服务器接收到online user 1023749时,ps aux|grep java内容为:
root 4925 43.6 28.4 8403444 4422824 ? Sl 15:26 62:53 java -server...

查看当前网络信息统计
ss -s
Total: 1024050 (kernel 1024084)
TCP: 1023769 (estab 1023754, closed 2, orphaned 0, synrecv 0, timewait 0/0), ports 12

Transport Total IP IPv6
* 1024084 - -
RAW 0 0 0
UDP 7 6 1
TCP 1023767 12 1023755
INET 1023774 18 1023756
FRAG 0 0 0

通过top查看一下
top -p 4925
top - 17:51:30 up 3:02, 4 users, load average: 1.03, 1.80, 1.19
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
Cpu0 : 0.9%us, 2.6%sy, 0.0%ni, 52.9%id, 1.0%wa, 13.6%hi, 29.0%si, 0.0%st
Cpu1 : 1.4%us, 4.5%sy, 0.0%ni, 80.1%id, 1.9%wa, 0.0%hi, 12.0%si, 0.0%st
Cpu2 : 1.5%us, 4.4%sy, 0.0%ni, 80.5%id, 4.3%wa, 0.0%hi, 9.3%si, 0.0%st
Cpu3 : 1.9%us, 4.4%sy, 0.0%ni, 84.4%id, 3.2%wa, 0.0%hi, 6.2%si, 0.0%st
Mem: 15554336k total, 15268728k used, 285608k free, 3904k buffers
Swap: 4194296k total, 1082592k used, 3111704k free, 37968k cached

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4925 root 20 0 8206m 4.2g 2220 S 3.3 28.4 62:53.66 java

四核都被占用了,每一个核心不太平均。这是在虚拟机中得到结果,可能真实服务器会更好一些。 因为不是CPU密集型应用,CPU不是问题,无须多加关注。
系统内存状况
free -m
total used free shared buffers cached
Mem: 15189 14926 263 0 5 56
-/+ buffers/cache: 14864 324
Swap: 4095 1057 3038

物理内存已经无法满足要求了,占用了1057M虚拟内存。
查看一下堆内存情况
jmap -heap 4925
Attaching to process ID 4925, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 23.21-b01

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 6442450944 (6144.0MB)
NewSize = 629145600 (600.0MB)
MaxNewSize = 629145600 (600.0MB)
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 1
PermSize = 52428800 (50.0MB)
MaxPermSize = 52428800 (50.0MB)
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 419430400 (400.0MB)
used = 308798864 (294.49354553222656MB)
free = 110631536 (105.50645446777344MB)
73.62338638305664% used
Eden Space:
capacity = 209715200 (200.0MB)
used = 103375232 (98.5863037109375MB)
free = 106339968 (101.4136962890625MB)
49.29315185546875% used
From Space:
capacity = 209715200 (200.0MB)
used = 205423632 (195.90724182128906MB)
free = 4291568 (4.0927581787109375MB)
97.95362091064453% used
To Space:
capacity = 209715200 (200.0MB)
used = 0 (0.0MB)
free = 209715200 (200.0MB)
0.0% used
concurrent mark-sweep generation:
capacity = 5813305344 (5544.0MB)
used = 4213515472 (4018.321487426758MB)
free = 1599789872 (1525.6785125732422MB)
72.48054631000646% used
Perm Generation:
capacity = 52428800 (50.0MB)
used = 5505696 (5.250640869140625MB)
free = 46923104 (44.749359130859375MB)
10.50128173828125% used

1439 interned Strings occupying 110936 bytes.

老生代占用内存为72%,较为合理,毕竟系统已经处理100万个连接。
再次断开所有测试端,看看系统内存(free -m)
total used free shared buffers cached
Mem: 15189 7723 7466 0 13 120
-/+ buffers/cache: 7589 7599
Swap: 4095 950 3145

记为list_free_2。
list_free_1和list_free_2两次都释放后的内存比较结果,系统可用物理已经内存已经降到7589M,先前可是7597M物理内存。
总之,我们的JAVA测试程序在内存占用方面已经,最低需要7589 + 950 = 8.6G内存为最低需求内存吧。
GC日志
我们在启动脚本处设置的一大串参数,到底是否达到目标,还得从gc日志处获得具体效果,推荐使用GCViewer。
GC事件概览:

其它:

总之:
只进行了一次Full GC,代价太高,停顿了12秒。
PartNew成为了停顿大户,导致整个系统停顿了41秒之久,不可接受。
当前JVM调优喜忧参半,还得继续努力等
小结
Java与与Erlang、C相比,比较麻烦的事情,需要在程序一开始就得准备好它的堆栈到底需要多大空间,换个说法就是JVM启动参数设置堆内存大小,设置合适的垃圾回收机制,若以后程序需要更多内存,需停止程序,编辑启动参数,然后再次启动。总之一句话,就是麻烦。单单JVM的调优,就得持续不断的根据检测、信息、日志等进行适当微调。

⑥ java 什么情况下使用 并发队列

并发队列是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael
& Scott算法上进行了一些修改。

入队列


入队列就是将入队节点添加到队列的尾部。为了方便理解入队时队列的变化,以及head节点和tair节点的变化,每添加一个节点我就做了一个队列的快照图。

publicEpoll(){

Node</e><e>h=head;

//p表示头节点,需要出队的节点

Node</e><e>p=h;

for(inthops=0;;hops++){

//获取p节点的元素

Eitem=p.getItem();

//如果p节点的元素不为空,使用CAS设置p节点引用的元素为null,如果成功则返回p节点的元素。

if(item!=null&&p.casItem(item,null)){

if(hops>=HOPS){

//将p节点下一个节点设置成head节点

Node</e><e>q=p.getNext();

updateHead(h,(q!=null)?q:p);

}

returnitem;

}

//如果头节点的元素为空或头节点发生了变化,这说明头节点已经被另外一个线程修改了。那么获取p节点的下一个节点

Node</e><e>next=succ(p);

//如果p的下一个节点也为空,说明这个队列已经空了

if(next==null){

//更新头节点。

updateHead(h,p);

break;

}

//如果下一个元素不为空,则将头节点的下一个节点设置成头节点

p=next;

}

returnnull;

}

首先获取头节点的元素,然后判断头节点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作将该节点的元素取走,如果不为空,则使用CAS的方式将头节点的引用设置成null,如果CAS成功,则直接返回头节点的元素,如果不成功,表示另外一个线程已经进行了一次出队操作更新了head节点,导致元素发生了变化,需要重新获取头节点。

⑦ java 多线程怎么深入

并发与并行

并行,表示两个线程同时做事情。

并发,表示一会做这个事情,一会做另一个事情,存在着调度。单核 CPU 不可能存在并行(微观上)。

image

以上就是原生线程池创建的核心原理。除了原生线程池之外并发包还提供了简单的创建方式,上面也说了它们是对原生线程池的一种包装,可以让开发者简单快捷的创建所需要的线程池。

Executors

newSingleThreadExecutor

创建一个线程的线程池,在这个线程池中始终只有一个线程存在。如果线程池中的线程因为异常问题退出,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newCachedThreadPool

可根据实际情况,调整线程数量的线程池,线程池中的线程数量不确定,如果有空闲线程会优先选择空闲线程,如果没有空闲线程并且此时有任务提交会创建新的线程。在正常开发中并不推荐这个线程池,因为在极端情况下,会因为 newCachedThreadPool 创建过多线程而耗尽 CPU 和内存资源。

newScheledThreadPool

此线程池可以指定固定数量的线程来周期性的去执行。比如通过 scheleAtFixedRate 或者 scheleWithFixedDelay 来指定周期时间。

PS:另外在写定时任务时(如果不用 Quartz 框架),最好采用这种线程池来做,因为它可以保证里面始终是存在活的线程的。

推荐使用 ThreadPoolExecutor 方式

在阿里的 Java 开发手册时有一条是不推荐使用 Executors 去创建,而是推荐去使用 ThreadPoolExecutor 来创建线程池。

这样做的目的主要原因是:使用 Executors 创建线程池不会传入核心参数,而是采用的默认值,这样的话我们往往会忽略掉里面参数的含义,如果业务场景要求比较苛刻的话,存在资源耗尽的风险;另外采用 ThreadPoolExecutor 的方式可以让我们更加清楚地了解线程池的运行规则,不管是面试还是对技术成长都有莫大的好处。

改了变量,其他线程可以立即知道。保证可见性的方法有以下几种:

  • volatile

  • 加入 volatile 关键字的变量在进行汇编时会多出一个 lock 前缀指令,这个前缀指令相当于一个内存屏障,内存屏障可以保证内存操作的顺序。当声明为 volatile 的变量进行写操作时,那么这个变量需要将数据写到主内存中。

    由于处理器会实现缓存一致性协议,所以写到主内存后会导致其他处理器的缓存无效,也就是线程工作内存无效,需要从主内存中重新刷新数据。

    ⑧ java并发包源码怎么读

    1. 各种同步控制工具的使用

    1.1 ReentrantLock

    ReentrantLock感觉上是synchronized的增强版,synchronized的特点是使用简单,一切交给JVM去处理,但是功能上是比较薄弱的。在JDK1.5之前,ReentrantLock的性能要好于synchronized,由于对JVM进行了优化,现在的JDK版本中,两者性能是不相上下的。如果是简单的实现,不要刻意去使用ReentrantLock。

    相比于synchronized,ReentrantLock在功能上更加丰富,它具有可重入、可中断、可限时、公平锁等特点。

    首先我们通过一个例子来说明ReentrantLock最初步的用法:

    package test;

    import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;

    @Override public void run() { for (int j = 0; j < 10000000; j++)
    { lock.lock(); try
    {
    i++;
    } finally
    { lock.unlock();
    }
    }
    }
    public static void main(String[] args) throws InterruptedException {
    Test test = new Test();
    Thread t1 = new Thread(test);
    Thread t2 = new Thread(test);
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);
    }

    }

    有两个线程都对i进行++操作,为了保证线程安全,使用了ReentrantLock,从用法上可以看出,与synchronized相比,ReentrantLock就稍微复杂一点。因为必须在finally中进行解锁操作,如果不在finally解锁,有可能代码出现异常锁没被释放,而synchronized是由JVM来释放锁。

    那么ReentrantLock到底有哪些优秀的特点呢?

    1.1.1 可重入

    单线程可以重复进入,但要重复退出

    lock.lock();
    lock.lock();try{
    i++;

    }
    finally{
    lock.unlock();
    lock.unlock();
    }

    由于ReentrantLock是重入锁,所以可以反复得到相同的一把锁,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放(重入锁)。这模仿了synchronized的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized块时,才释放锁。

    public class Child extends Father implements Runnable{ final static Child child = new Child();//为了保证锁唯一
    public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(child).start();
    }
    }
    public synchronized void doSomething() {
    System.out.println("1child.doSomething()");
    doAnotherThing(); // 调用自己类中其他的synchronized方法
    }
    private synchronized void doAnotherThing() { super.doSomething(); // 调用父类的synchronized方法
    System.out.println("3child.doAnotherThing()");
    }
    @Override
    public void run() {
    child.doSomething();
    }
    }class Father { public synchronized void doSomething() {
    System.out.println("2father.doSomething()");
    }
    }

    我们可以看到一个线程进入不同的synchronized方法,是不会释放之前得到的锁的。所以输出还是顺序输出。所以synchronized也是重入锁

    输出:

    1child.doSomething()
    2father.doSomething()
    3child.doAnotherThing()
    1child.doSomething()
    2father.doSomething()
    3child.doAnotherThing()
    1child.doSomething()
    2father.doSomething()
    3child.doAnotherThing()
    ...

    1.1.2.可中断

    与synchronized不同的是,ReentrantLock对中断是有响应的。中断相关知识查看[高并发Java 二] 多线程基础

    普通的lock.lock()是不能响应中断的,lock.lockInterruptibly()能够响应中断。

    我们模拟出一个死锁现场,然后用中断来处理死锁

    package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
    { this.lock = lock;
    } @Override
    public void run()
    { try
    { if (lock == 1)
    {
    lock1.lockInterruptibly(); try
    {
    Thread.sleep(500);
    } catch (Exception e)
    { // TODO: handle exception
    }
    lock2.lockInterruptibly();
    } else
    {
    lock2.lockInterruptibly(); try
    {
    Thread.sleep(500);
    } catch (Exception e)
    { // TODO: handle exception
    }
    lock1.lockInterruptibly();
    }
    } catch (Exception e)
    { // TODO: handle exception
    } finally
    { if (lock1.isHeldByCurrentThread())
    {
    lock1.unlock();
    } if (lock2.isHeldByCurrentThread())
    {
    lock2.unlock();
    }
    System.out.println(Thread.currentThread().getId() + ":线程退出");
    }
    } public static void main(String[] args) throws InterruptedException {
    Test t1 = new Test(1);
    Test t2 = new Test(2);
    Thread thread1 = new Thread(t1);
    Thread thread2 = new Thread(t2);
    thread1.start();
    thread2.start();
    Thread.sleep(1000); //DeadlockChecker.check();
    } static class DeadlockChecker
    { private final static ThreadMXBean mbean = ManagementFactory
    .getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
    { @Override
    public void run()
    { // TODO Auto-generated method stub
    while (true)
    { long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
    {
    ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
    { for (int i = 0; i < threadInfos.length; i++)
    { if(t.getId() == threadInfos[i].getThreadId())
    {
    t.interrupt();
    }
    }
    }
    } try
    {
    Thread.sleep(5000);
    } catch (Exception e)
    { // TODO: handle exception
    }
    }

    }
    };
    public static void check()
    {
    Thread t = new Thread(deadlockChecker);
    t.setDaemon(true);
    t.start();
    }
    }

    }

    上述代码有可能会发生死锁,线程1得到lock1,线程2得到lock2,然后彼此又想获得对方的锁。

    我们用jstack查看运行上述代码后的情况

    下面举个例子:

    package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
    { this.soldier = soldier; this.cyclic = cyclic;
    } @Override
    public void run()
    { try
    { //等待所有士兵到齐
    cyclic.await();
    dowork(); //等待所有士兵完成工作
    cyclic.await();
    } catch (Exception e)
    { // TODO Auto-generated catch block
    e.printStackTrace();
    }

    } private void dowork()
    { // TODO Auto-generated method stub
    try
    {
    Thread.sleep(3000);
    } catch (Exception e)
    { // TODO: handle exception
    }
    System.out.println(soldier + ": done");
    } public static class BarrierRun implements Runnable
    { boolean flag; int n; public BarrierRun(boolean flag, int n)
    { super(); this.flag = flag; this.n = n;
    } @Override
    public void run()
    { if (flag)
    {
    System.out.println(n + "个任务完成");
    } else
    {
    System.out.println(n + "个集合完成");
    flag = true;
    }

    }

    } public static void main(String[] args)
    { final int n = 10;
    Thread[] threads = new Thread[n]; boolean flag = false;
    CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
    System.out.println("集合"); for (int i = 0; i < n; i++)
    {
    System.out.println(i + "报道");
    threads[i] = new Thread(new Test("士兵" + i, barrier));
    threads[i].start();
    }
    }

    }

    打印结果:

    集合

    士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10个任务完成

    1.7 LockSupport

    提供线程阻塞原语

    和suspend类似

    LockSupport.park();
    LockSupport.unpark(t1);

    与suspend相比不容易引起线程冻结

    LockSupport的思想呢,和Semaphore有点相似,内部有一个许可,park的时候拿掉这个许可,unpark的时候申请这个许可。所以如果unpark在park之前,是不会发生线程冻结的。

    下面的代码是[高并发Java 二] 多线程基础中suspend示例代码,在使用suspend时会发生死锁。

    而使用LockSupport则不会发生死锁。

    另外

    park()能够响应中断,但不抛出异常。中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志。

    在JDK当中有大量地方使用到了park,当然LockSupport的实现也是使用unsafe.park()来实现的。

    public static void park() { unsafe.park(false, 0L);
    }

    1.8 ReentrantLock 的实现

    下面来介绍下ReentrantLock的实现,ReentrantLock的实现主要由3部分组成:

    • CAS状态

    • 等待队列

    • park()

    • ReentrantLock的父类中会有一个state变量来表示同步的状态

    • /**

    • * The synchronization state.

    • */

    • private volatile int state;

    • 通过CAS操作来设置state来获取锁,如果设置成了1,则将锁的持有者给当前线程

    • final void lock() { if (compareAndSetState(0, 1))

    • setExclusiveOwnerThread(Thread.currentThread()); else

    • acquire(1);

    • }

    • 如果拿锁不成功,则会做一个申请

    • public final void acquire(int arg) { if (!tryAcquire(arg) &&

    • acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

    • selfInterrupt();

    • }

    • 首先,再去申请下试试看tryAcquire,因为此时可能另一个线程已经释放了锁。

      如果还是没有申请到锁,就addWaiter,意思是把自己加到等待队列中去

      其间还会有多次尝试去申请锁,如果还是申请不到,就会被挂起

    • private final boolean parkAndCheckInterrupt() {

    • LockSupport.park(this); return Thread.interrupted();

    • }

    • 同理,如果在unlock操作中,就是释放了锁,然后unpark,这里就不具体讲了。

      2. 并发容器及典型源码分析

      2.1ConcurrentHashMap

      我们知道HashMap不是一个线程安全的容器,最简单的方式使HashMap变成线程安全就是使用Collections.synchronizedMap,它是对HashMap的一个包装

    • public static Map m=Collections.synchronizedMap(new HashMap());

    • 同理对于List,Set也提供了相似方法。

      但是这种方式只适合于并发量比较小的情况。

      我们来看下synchronizedMap的实现

      它会将HashMap包装在里面,然后将HashMap的每个操作都加上synchronized。

      由于每个方法都是获取同一把锁(mutex),这就意味着,put和remove等操作是互斥的,大大减少了并发量。

      下面来看下ConcurrentHashMap是如何实现的

      在ConcurrentHashMap内部有一个Segment段,它将大的HashMap切分成若干个段(小的HashMap),然后让数据在每一段上Hash,这样多个线程在不同段上的Hash操作一定是线程安全的,所以只需要同步同一个段上的线程就可以了,这样实现了锁的分离,大大增加了并发量。

      在使用ConcurrentHashMap.size时会比较麻烦,因为它要统计每个段的数据和,在这个时候,要把每一个段都加上锁,然后再做数据统计。这个就是把锁分离后的小小弊端,但是size方法应该是不会被高频率调用的方法。

      在实现上,不使用synchronized和lock.lock而是尽量使用trylock,同时在HashMap的实现上,也做了一点优化。这里就不提了。

      2.2BlockingQueue

      BlockingQueue不是一个高性能的容器。但是它是一个非常好的共享数据的容器。是典型的生产者和消费者的实现。

    ⑨ Java并发框架都有哪些

    java的并发工具类主要都在 java.util.concurrent 包,主要包括
    locks部分:显式锁(互斥锁和速写锁)相关;
    atomic部分:原子变量类相关,是构建非阻塞算法的基础;
    executor部分:线程池相关;
    collections部分:并发容器相关;
    tools部分:同步工具相关,如信号量、闭锁、栅栏等功能
    自己可以深究下下面的源码和实现

    ⑩ 大神觉得学习java,应该用什么书

    1.java基础。

    2.html/css/js ,servlet/jsp(j2ee) 以及mysql数据库应用。

    3.学习框架,一般ssm(spring + springmvc+mybatis) 。

    4.了解maven工程。

    5.看完《java编程思想》。

    6.1-2年看《大话设计模式》《head first》。

    7.代码编写优化《重构改善既有代码的设计》,《effective java》。

    8.2-3年《深入理解java虚拟机》。

    9.《java并发编程实战》.

    10.查看java类库,jdk源码。

    11.其他技术,比如大数据,分布式缓存,分布式消息服务,分布式计算,软负载均衡等,深入了解其中一项再考虑其他。

    12.《深入理解计算机系统》(比较适合java Web开发和APP后端开发),《tcp/ip详解 卷一,二,三》(适合网络编程),《数据结构与算法》(适合计算机研究工作,如分布式计算)。

    13.这个时候可以去Github创建一个属于自己的开源项目,打造自己的产品。

    热点内容
    c语言停留 发布:2025-01-16 17:51:13 浏览:959
    一机一码文件加密 发布:2025-01-16 17:36:19 浏览:415
    软件登录密码用户名是什么 发布:2025-01-16 17:11:52 浏览:111
    我的世界建服务器该注意什么 发布:2025-01-16 17:06:40 浏览:568
    php随机小数 发布:2025-01-16 17:02:48 浏览:139
    linuxterminal 发布:2025-01-16 17:02:04 浏览:249
    如何配置i5的电脑 发布:2025-01-16 17:00:21 浏览:264
    压缩空气泄漏 发布:2025-01-16 16:55:51 浏览:259
    皖教育密码是多少 发布:2025-01-16 16:50:59 浏览:451
    有专用dhcp服务器无法获取ip 发布:2025-01-16 16:48:58 浏览:810