- 浏览: 183973 次
- 性别:
- 来自: 杭州
博客专栏
-
Java技术分享
浏览量:0
文章分类
最新评论
-
masuweng:
学习了,学习了
mybatis是如何防止SQL注入的 -
somefuture:
终于知道了#$的区别
mybatis是如何防止SQL注入的 -
masuweng:
...
tomct处理请求的流程 -
zhp8341:
masuweng 写道寻求cas的更多例子, http://w ...
JUC之CAS -
臻是二哥:
java.util.concurrent包中到处都使用了CAS ...
JUC之CAS
不知道各位童鞋是否干过在程序主进程中嵌入IO操作这样“任性”的事情,笔者曾经干过,而且那时候还觉得自己很棒。没办法,那时候还年轻啊。后来随着学习的深入,知道了有nio这种神一样的东西。但如果有其他方案,至少笔者是不会使用nio这个类的。毕竟程序的易读性还是很重要滴。多数时候,我们完全可以使用多线程和IO阻塞来代替非阻塞IO。
最近读了下经典《Java并发编程实践》,大师的书的确很棒,好了不说废话了,来写下我的笔记吧。
并发编程主要需要解决安全性,活跃性和性能的问题。下面是个人的理解:
1 安全性:程序可以确定的按照我们希望的时序执行。
2 活跃性:程序最终会输出正确的结果
3 性能:程序多长时间能输出正确的结果。
对于程序设计人员,我们需要在上面的3点之间寻找一种平衡,一种在满足安全性基础之上的平衡。
首先,我觉得有一个问题需要解决,那就是什么时候使用并发编程:并发编程一定是在需要的时候使用,什么是需要的时候?比如有一天,你的leader和你说,系统太慢了,以至于用户都快跑光了(哈哈,有点夸张),这个时候你就需要考虑一下是否可以使用多线程来做点什么。如果你确实发现了代码中有太多可以用多线程提升的地方,那么,。just do it。否则,还是不要轻易改变了,毕竟“稳定压倒一切吗”。先保证稳定在考虑性能。
当决定使用多线程的时候,我们就需要考虑安全性,活跃性以及性能了。
所谓的安全性,就是要求我们能够编写线程安全的代码,他的核心在于处理那些可以被多个线程共享的可变的变量。就像我在黑体部分说的,需要考虑线程安全的情况,一定是有下列3个条件:
1 一个可以被多个线程访问的变量
2 这个变量是共享的
3 这个变量的值是可以改变的
但你的程序中出现了这3个情况的时候,那么要小心了,需要考虑线程安全问题了。说白了,就是对于这个共享变量的访问时候的同步问题。就是常见的“读-改-写”和“核对后执行”情况。
比如我写了一个类:
在这个例子中,MyThread类的变量count是可以被多个线程访问的,是共享的,是可变的(瞧,满足3个条件),我们需要考虑线程安全问题了。这是一个典型的“读-改-写”的问题,理论上我当然希望每个线程都能一口气执行完count++这条语句,但事实常常不是这样,可能thread-1刚刚读取到count为0,CPU就调度给了thread-2,这个时候thread-2读取到的count也是0,很显然,程序没有按照我们希望的时序执行,我们说他是不安全的。
我们可以使用线面的方式来让他变成线程安全的.
但是有些时候,并不总是有这样的第三方的类可以给我们使用的。
可能我们写了一个类,来根据一个请求的1-9的阿拉伯数字返回对应的大写汉字
尽管service方法被多个线程执行,但不存在安全性的问题(没有多个线程共享的变量);这个时候,如果为了提高性能,需要将上一次请求的数据存储起来,我们来看:
在上面的代码中,出现了多个线程可以访问可变的的共享变量num和str,这时候需要考虑线程安全的问题了。有个最简单的方案将他变为线程安全的:
程序设计人员不去具体分析哪些代码是需要同步的,而是同步一个大的区域,讲所有共享变量圈在里面,当然这样做实现了线程安全。但是没有考虑性能的问题。假设getHanziByNum()是一个非常耗时间的操作,那么同步方法service的执行时间将会非常慢,而整个方法有事同步的,很可能我们的并发代码 运行的性能比串行的还要差(创建和销毁线程需要时间,尽管很短)。因此,我们需要一些更加细粒度的同步,而不是像上面的粗粒度的同步。
上面的类是线程安全的,显然他比之前一个在性能上会更有优势。所以在解决多线程安全性的问题上,关于同步的粒度的大小需要我们仔细的研究,力度大一点可能实现起来简单,但是性能不一定太好。粒度太小的话,可能实现起来难度大,容易出错,怎么做到合适,需要我们自己权衡。
这个时候,如果我还想保存命中的变量
可以看到,所有共享变量出现的地方都被加了锁(即同步操作),只不过同步的力度不同罢了。
综上所述,关于并发,我们的思路大概是这样的:
1 是否使用并发
2 如果使用用并发,哪些共享变量需要考虑安全性
3 对于需要考虑安全性的那些共享变量,要记得为每一个加锁
4 加锁的粒度粗细的权衡
最近读了下经典《Java并发编程实践》,大师的书的确很棒,好了不说废话了,来写下我的笔记吧。
并发编程主要需要解决安全性,活跃性和性能的问题。下面是个人的理解:
1 安全性:程序可以确定的按照我们希望的时序执行。
2 活跃性:程序最终会输出正确的结果
3 性能:程序多长时间能输出正确的结果。
对于程序设计人员,我们需要在上面的3点之间寻找一种平衡,一种在满足安全性基础之上的平衡。
首先,我觉得有一个问题需要解决,那就是什么时候使用并发编程:并发编程一定是在需要的时候使用,什么是需要的时候?比如有一天,你的leader和你说,系统太慢了,以至于用户都快跑光了(哈哈,有点夸张),这个时候你就需要考虑一下是否可以使用多线程来做点什么。如果你确实发现了代码中有太多可以用多线程提升的地方,那么,。just do it。否则,还是不要轻易改变了,毕竟“稳定压倒一切吗”。先保证稳定在考虑性能。
当决定使用多线程的时候,我们就需要考虑安全性,活跃性以及性能了。
所谓的安全性,就是要求我们能够编写线程安全的代码,他的核心在于处理那些可以被多个线程共享的可变的变量。就像我在黑体部分说的,需要考虑线程安全的情况,一定是有下列3个条件:
1 一个可以被多个线程访问的变量
2 这个变量是共享的
3 这个变量的值是可以改变的
但你的程序中出现了这3个情况的时候,那么要小心了,需要考虑线程安全问题了。说白了,就是对于这个共享变量的访问时候的同步问题。就是常见的“读-改-写”和“核对后执行”情况。
比如我写了一个类:
@UnThreadSafe class MyThread extends Thread { private int count=0; public void run(){ count++; System.out.println("counter="+count); } }
在这个例子中,MyThread类的变量count是可以被多个线程访问的,是共享的,是可变的(瞧,满足3个条件),我们需要考虑线程安全问题了。这是一个典型的“读-改-写”的问题,理论上我当然希望每个线程都能一口气执行完count++这条语句,但事实常常不是这样,可能thread-1刚刚读取到count为0,CPU就调度给了thread-2,这个时候thread-2读取到的count也是0,很显然,程序没有按照我们希望的时序执行,我们说他是不安全的。
我们可以使用线面的方式来让他变成线程安全的.
@ThreadSafe class MyThread extends Thread { private final AtomicLong count=new AtomicLong(0); public void run(){ int tmp=count.incrementAndGet(); System.out.println("counter="+tmp); } }
但是有些时候,并不总是有这样的第三方的类可以给我们使用的。
可能我们写了一个类,来根据一个请求的1-9的阿拉伯数字返回对应的大写汉字
public class MyServlet implements Servlet { public void service(ServletRequest req,ServletResponse res){ int i=getNum(req); String str=getHanziByNum(i); responseHanzi(res,i); } }
尽管service方法被多个线程执行,但不存在安全性的问题(没有多个线程共享的变量);这个时候,如果为了提高性能,需要将上一次请求的数据存储起来,我们来看:
@UnThreadSafe public class MyServlet implements Servlet { private int num; private String str; public void service(ServletRequest req,ServletResponse res){ int i=getNum(req); if(i==num) responseHanzi(res,str); else String str1=getHanziByNum(i); num=i; str=str1; responseHanzi(res,str1); } }
在上面的代码中,出现了多个线程可以访问可变的的共享变量num和str,这时候需要考虑线程安全的问题了。有个最简单的方案将他变为线程安全的:
@ThreadSafe public class MyServlet implements Servlet { private int num; private String str; public synchronized void service(ServletRequest req,ServletResponse res){ int i=getNum(req); if(i==num) responseHanzi(res,str); else String str1=getHanziByNum(i); num=i; str=str1; responseHanzi(res,str1); } }
程序设计人员不去具体分析哪些代码是需要同步的,而是同步一个大的区域,讲所有共享变量圈在里面,当然这样做实现了线程安全。但是没有考虑性能的问题。假设getHanziByNum()是一个非常耗时间的操作,那么同步方法service的执行时间将会非常慢,而整个方法有事同步的,很可能我们的并发代码 运行的性能比串行的还要差(创建和销毁线程需要时间,尽管很短)。因此,我们需要一些更加细粒度的同步,而不是像上面的粗粒度的同步。
@ThreadSafe public class MyServlet implements Servlet { private int num; private String str; public void service(ServletRequest req,ServletResponse res){ int i=getNum(req); String str1=null; synchronized(this){ if(i==num) str1=str; } if(str1=null){ str1=getHanziByNum(i); synchronized(this){ num=i; str=str1; } } responseHanzi(res,str1); } }
上面的类是线程安全的,显然他比之前一个在性能上会更有优势。所以在解决多线程安全性的问题上,关于同步的粒度的大小需要我们仔细的研究,力度大一点可能实现起来简单,但是性能不一定太好。粒度太小的话,可能实现起来难度大,容易出错,怎么做到合适,需要我们自己权衡。
这个时候,如果我还想保存命中的变量
@ThreadSafe public class MyServlet implements Servlet { private int num; private String str; private int hits; private int totalHits; public synchronized double getHitRadio{ return hits/totalHits; } public void service(ServletRequest req,ServletResponse res){ int i=getNum(req); String str1=null; synchronized(this){ totalHits++; if(i==num){ str1=str; hits++; } } if(str1=null){ str1=getHanziByNum(i); synchronized(this){ num=i; str=str1; } } responseHanzi(res,str1); } }
可以看到,所有共享变量出现的地方都被加了锁(即同步操作),只不过同步的力度不同罢了。
综上所述,关于并发,我们的思路大概是这样的:
1 是否使用并发
2 如果使用用并发,哪些共享变量需要考虑安全性
3 对于需要考虑安全性的那些共享变量,要记得为每一个加锁
4 加锁的粒度粗细的权衡
发表评论
-
精心准备的讲解Java多线程的知乎Live
2018-09-02 21:39 704花了一个月的时间,结合自己的理解,制作了一个以图片的方式讲解J ... -
在知乎推出Java求职类专栏文章
2018-08-27 12:06 790从昨天起,笔者开始在知乎推出Java求职类专栏文章,主要涉及一 ... -
JUC之AQS
2016-07-27 15:49 1213AQS是同步框架,它进行 ... -
JUC之CAS
2016-07-27 10:56 1564JUC是java.util.concurrent包 ... -
JAVA并发-线程状态和线程组
2016-07-18 14:14 1273在Java中,线程有6种基本状态,理解这6种基本状态之间的关系 ... -
JAVA并发-条件队列
2016-07-02 09:57 1115在JVM系列博客http://yizhen ... -
JAVA并发-中断处理和任务取消
2016-06-27 22:42 1201中断处理 在java程序中 ... -
JAVA并发-显式锁(二)
2016-06-06 09:25 1024在上一篇博客《JAVA并发-显式锁(一)》中介绍了Lock和R ... -
JAVA并发-显式锁(一)
2016-06-05 20:40 891JAVA语言除了提供内置锁synchronized,还在JDK ... -
JAVA并发-减少锁的竞争
2016-06-04 16:01 1853降低锁的竞争可以提高 ... -
JAVA并发-3种典型的死锁
2016-06-03 19:54 1621在JAVA并发编程中,我们 ... -
JAVA并发-3种典型的死锁
2016-06-03 10:02 10在JAVA并发编程中,我们 ... -
JAVA并发-ThreadPoolExecutor线程池的使用
2016-05-30 16:19 2356在博客JAVA并发-Executor任务执行框架中曾说过,Ex ... -
JAVA并发-Executor任务执行框架
2016-05-27 19:10 432首先介绍两个重要的接 ... -
JAVA并发-DCL与JMM
2016-04-26 19:21 1576首先必须声明,在volatil ... -
JAVA并发- 典型连接池的实现
2016-04-22 10:44 1140package com.xyz.connpool; pu ... -
JAVA并发-构建可靠的结果缓存
2016-04-10 16:58 55在学习了前面关于多线程的博客之后,下面来介绍一个构建可靠结果缓 ... -
JAVA并发-并发编程常用类
2016-04-08 11:07 991在讲同步工具类之前,笔者想先介绍一下Runnable,Call ... -
JAVA并发-同步容器和并发容器
2016-04-07 19:02 698常见的同步容器类包括V ... -
JAVA并发-开发线程安全类
2016-04-01 10:23 840我们知道,面向对象的 ...
相关推荐
java高级技术JUC高并发编程教程2021(1.5G) 〖课程介绍〗: java高级技术JUC高并发编程教程2021(1.5G) 〖课程目录〗: 01-JUC高并发编程-课程介绍.mp4 02-JUC高并发编程-JUC概述和进程线程概念(1).mp4 03-JUC...
并发编程:编写多线程代码,解决多线程带来的问题 为什么要学并发编程? 首先,来看一个案例:手写网站服务器案例。 高性能应用程序的一把钥匙,应用程序的翅膀,面试高频的考点 中间件几乎都是多线程应用:MySQL、...
线程的状态以及各状态之间的转换详解.mp4 线程的初始化,中断以及其源码讲解.mp4 ...happens-before简单概述.mp4 锁的内存语义.mp4 volatile内存语义.mp4 final域的内存语义.mp4 实战:问题定位.mp4
第59节happens-before简单概述00:15:17分钟 | 第60节锁的内存语义00:13:54分钟 | 第61节volatile内存语义00:12:04分钟 | 第62节final域的内存语义00:34:07分钟 | 第63节实战:问题定位00:07:48分钟 |
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...
第8章 多线程编程 第1页 本章概述 本章的学习目标 主要内容 Java程序设计案例教程-第8章-多线程编程全文共36页,当前为第1页。 本章概述 前面我们所开发的程序大多是单线程的,即一个程序只有一条从头到尾的执行路线...
并发编程概述: 引入并发编程的基本概念,解释为什么多线程编程在现代应用中至关重要。 线程和进程: 解释线程和进程的区别,介绍线程的概念、特点和创建方式。 共享资源与竞态条件: 详解共享资源在多线程环境中的...
这份资源为您提供了关于 Java 并发编程的全面讲解,着重介绍了 JUC(java.util.concurrent)库中的核心概念、工具和最佳实践。通过深入学习,您将能够更好地理解并发编程的挑战,掌握构建高性能、高可伸缩性的并发...
java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个...
NULL 博文链接:https://lingqi1818.iteye.com/blog/644940
本资源致力于向您介绍 Java 并发编程中的线程基础,涵盖了多线程编程的核心概念、线程的创建和管理,以及线程间通信的基本方法。通过深入学习,您将建立扎实的多线程编程基础,能够更好地理解和应用多线程编程。 多...
第59节happens-before简单概述00:15:17分钟 | 第60节锁的内存语义00:13:54分钟 | 第61节volatile内存语义00:12:04分钟 | 第62节final域的内存语义00:34:07分钟 | 第63节实战:问题定位00:07:48分钟 |
java.util.concurrent 在并发编程中很常用的实用工具类。 java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类...
概述 三种性质 o可见性 :一个线程对共享变量的修改,另一个线程能立刻看到。 缓存 可导致可见性问题。 o原子性 :一个或多个CPU执行操作不被中断。 线程切换 可导致原子性问题。 o有序性 :编译器优化可能...
java多线程编程 纯手写实现SpringIOC实现过程 JEE企业级开发(企业级项目开发权威指南) 网络爬虫之JAVA正则表达式 手写springMVC框架 老司机带你透析springMVC内部实现方式 打造高效代码结构(java性能优化) 新版本...
哈希值 LinkedHashSet TreeSet 自然排序Comparable 比较器排序Comparator Set集合 并发修改异常 LinkedList集合 ArrayList集合 List集合 Collection集合概述 冒泡排序 Object 异常 Math 包装类 Calendar类 ...
编程进行概述,阐述组件、事件、布局管理器的基本概念,然后依次对 Swing 组件、菜单、对话框、绘图等进行介绍。 第三部分(第22~27章)探究 Java API 库的部分内容,其中包括 java.lang 包和 java.util 包的大部分...