废话

众所周知,在Java的知识体系中,并发编程是非常重要的一环,也是面试的必问题,一个好的Java程序员是必须对并发编程这块有所了解的。为了追求成为一个好的Java程序员,我决定从今天开始死磕Java的并发编程,尽量弥补自己在这方面的知识缺陷。

并发必须知道的概念

在深入学习并发编程之前,我们需要了解几个基本的概念。

同步和异步

同步和异步用请求返回调用的方式来理解相对简单。

同步:可以理解为发出一个请求后,必须等待返回结果才能执行下面的操作。

异步:请求发出后,不需要等待返回结果,可以继续执行后续操作,异步请求更像是在另一个 “空间” 中处理请求的结果,这个过程不会影响请求方的其他操作。

举个生活中的例子,比如我们去实体店买衣服,挑选完款式后下单让售货员去仓库拿货,在售货员拿货的过程你需要在店里等待,直到售货员把衣服交给你后才算购物成功,这就相当于同步的过程。

不过,如果是在网上购物的话,我们只需下单并完成支付,对我们来说整个购物过程就算完成了。网上的商家接到订单会帮我们加紧安排送货,这段时间我们可以去做其他的事,比如去外面打个篮球之类的。等送货上门并签收商品就完事了,这个过程就相当于异步。

并发和并行

并发和并行的功能很相似,两者都可以表示多个任务一起执行的情况,但本质上两者其实是有区别的。

严格意义上来说,并行的多任务是真实的同时执行,而并发更多的情况是任务之间交替执行,系统不停的在多个任务间切换执行,也就是 “串行” 执行。

最直接的例子的就是我们的计算机系统,在单核CPU时代,系统表面上能同时进行多任务处理,比如听歌的同时又浏览网页,但真实环境中这些任务不可能是真实并行的,因为一个CPU一次只能执行一条指令,这种情况就是并发,系统看似能处理多任务是因为不停的切换任务,但因为时间非常短,所以在我们的感官来说就是同时进行的。而计算机系统真实的并行是随着多核CPU的出现才有的。

临界区

临界区表示公共资源或是共享数据,可以被多个线程使用。但是每次只能有一个线程使用它,一旦临界区的资源被占用,其他线程就必须等到资源释放后才能继续使用该资源。在Java程序开发中,对于这样的资源一般都需要做同步的操作,例如下面的这段代码,用的就是synchronized关键字来对临界区资源进行同步

public class SyncTest implements Runnable {

    //临界区资源
public static SyncTest instance = new SyncTest(); @Override
public void run() {
synchronized (instance) { }
} public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new SyncTest());
Thread t2 = new Thread(new SyncTest());
t1.start();
t2.start();
t1.join();
t2.join();
}
}

阻塞和非阻塞

阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区的资源,那么其他需要这个资源的线程就必须等待。等待的过程会使线程挂起,也就是阻塞。如果临界区的资源一直不释放的话,那么其他阻塞的线程就都不能工作了。

非阻塞则相反,强调的是线程之间并不互相妨碍,所有的线程都会不断尝试向前执行。

死锁、饥饿和活锁

这三种情况表示的是多线程间的活跃状态,对于线程来说,以上的情况都是 “非友好” 的状态。

1、死锁一般是指两个或者两个以上的线程互相持有对方所需的资源,并且永远在等待对方释放的一种阻塞状态。例如有两个线程A和B同时共享临界区的资源C,当A占用C时,B处于阻塞状态,然而A的释放需要用到B的资源,这样一来,就变成了A一直在等待B,B也一直在等待A,互相之间永远在等待对方释放的状态。

一般来说,死锁的发生是由于程序的设计不合理导致,而且死锁很难解决,最好的方式就是预防

2、饥饿是指某一个或者多个线程因为种种原因无法获得所需的资源,导致一直无法执行。比如它的线程优先级太低,而高优先级的线程不断抢占它所需的资源,导致低优先级资源无法工作。

3、活锁的情况是线程一种非常有趣的情况,在生活中我们可能会碰到这样的情况,那就是出门的时候可能会遇到有人要进门,你打算让他先进门,他又打算让你先出门,结果,两个人都互相退后了,然后你打算先出门时对方也向前一步,来来回回就一直卡在门口。当然,这种事情正常人很快就能解决,但如果是线程碰到就没那么幸运了。

如果两个线程占用着公共的资源,并且秉承着 “谦让” 的原则,主动把资源让给他人使用,你让我也让,这样就造成资源在两个线程间不断跳动但线程之间都拿不到资源的情况,这样的情况就是活锁了。

线程安全

线程安全指的是多线程的安全。如果一段程序可以保证被多线程访问后仍能保持正确性,那么程序就是线程安全的。一般来说,线程安全注重的是多线程开发中的共享数据的安全。就比如下面这段代码:

public class ThreadSafety implements Runnable{
//共享数据
public static int i = 0; public void increase(){
for (int j= 0;j<10; j++){
i++;
}
} @Override
public void run() {
increase();
} public static void main(String[] args) throws Exception{
ThreadSafety demo = new ThreadSafety();
Thread t1 = new Thread();
Thread t2 = new Thread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
} }

两个线程 t1 和 t2 同时开启,执行run方法,在我们的预想中,如果是线程安全的话,那么main的执行结果应该是20,但是因为 i 是共享数据,而程序没有对 i 的操作做同步的处理,最终运行的结果并不是20,所以这种情况就不是线程安全的情况。

解决的办法也比较简单,可以利用synchronized关键字来修饰方法或代码块,这部分的知识也是并发编程中非常重要的一块,当然,本文就不探究了,之后单独写篇文章出来细说。

参考:《实战Java:高并发程序设计》

Java并发编程:什么是线程安全,以及并发必须知道的几个概念的更多相关文章

  1. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  2. Java并发编程的艺术读书笔记(2)-并发编程模型

    title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...

  3. Java并发编程的艺术读书笔记(1)-并发编程的挑战

    title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...

  4. java并发编程笔记(九)——多线程并发最佳实践

    java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...

  5. [书籍翻译] 《JavaScript并发编程》第七章 抽取并发逻辑

    本文是我翻译<JavaScript Concurrency>书籍的第七章 抽取并发逻辑,该书主要以Promises.Generator.Web workers等技术来讲解JavaScrip ...

  6. 【java并发编程实战】-----线程基本概念

    学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...

  7. Java并发编程:进程和线程的由来(转)

    Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...

  8. 转:【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)

      Executor框架简介 在Java5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.coc ...

  9. 【Java并发编程六】线程池

    一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...

  10. 【Java并发编程一】线程安全和共享对象

    一.什么是线程安全 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的 ...

随机推荐

  1. ctags的如何生成tags文件

    tags 在使用vim编程和浏览代码是非常有用.可以用CTRL+]和CTRL+t 来回跳转关键字.先生成自己工作目录的tags.最简单粗暴用法: $cd yourwork $ctags -R * 这样 ...

  2. [Word]让字符重合显示

    某些时候需要让字符重合显示,比如您好二字,显示为: 需要用到word的Advance域,他可以让后面的文字上下左右移动一定的磅. 譬如上面你好的显示:word中域代码为: 意思是好字向left移动了2 ...

  3. 关于C++中字符串与数字的互相转换

    方法搬自博客:  Qt数据类型转换  侵删 1. 把QString转换为double类型 //法一: QString str="123.45"; double val=str.to ...

  4. [JSOI2018]列队(主席树)

    跟上次那道列队不一样,但都是九条可怜...(吉老师太强了) 在主席树上统计答案,因为值域只有 \(10^6\) 甚至不用离散化... \(Code\ Below:\) #include <bit ...

  5. 19_python_反射

    一.内置函数(补充)          1.issubclass() -- 方法用于判断参数 class 是否是类型参数 classinfo 的子类.   语法格式:issubclass(class, ...

  6. 常用的SQL调优

    1. 不要使用 select * ,使用select *的话会增加解析的时间,另外会把不需要的数据也给查询出来,数据传输也是耗费时间的. 2.  避免在 where 子句中使用 or 来连接条件,可以 ...

  7. [LeetCode]最长回文子串 java

    题目: 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: ...

  8. [原创]K8飞刀 新增Acunetix WVS 远程漏洞 反制黑客

    工具: K8飞刀20150603组织: K8搞基大队[K8team]作者: K8拉登哥哥博客: http://qqhack8.blog.163.com发布: 2015/6/3 20:41:29 简介: ...

  9. Django两天搭建个人博客

    传送门:https://github.com/1417766861/django-blog(可直接运行,上面有步骤) 效果: 首页: 侧栏: 详情页面: 修改头像,资料,文章发布: 支持添加标签拖拽 ...

  10. Solr6.5配置中文分词IKAnalyzer和拼音分词pinyinAnalyzer (二)

    之前在 Solr6.5在Centos6上的安装与配置 (一) 一文中介绍了solr6.5的安装.这篇文章主要介绍创建Solr的Core并配置中文IKAnalyzer分词和拼音检索. 一.创建Core: ...