java多线程-01-基本概念

[TOC]

声明

该系列文章只是记录本人回顾java多线程编程时候记录的笔记。文中所用语言并非严谨的专业术语(太严谨的术语其实本人也不会……)。难免有理解偏差的地方,欢迎指正。
另外,大神请绕路。不喜勿喷。
毕竟好记性不如烂笔头嘛,而且许多东西只要不是你经常用的最终都会一丢丢一丢丢地给忘记。

1 几个名词

1.1 多线程

简单理解,就是同一段程序同时以多条路径执行。
而这里的“同时”一词表示的含义就看你具体的机器配置了:你有多个CPU或者多核,当然是同时并发执行了;如果你是单核单CPU那就只能是CPU时间片的切换来执行了。

执行以下代码,可以看看一个普通java程序执行的时候到底有几个线程在运行:

public static void main(String[] args) {
    List<ThreadInfo> infos = Arrays.asList(//
            ManagementFactory.getThreadMXBean().dumpAllThreads(false, false)//
    );
    infos.stream().forEach(e -> {
        System.out.println(e.getThreadId() + ":" + e.getThreadName());
    });
}

在本人的机器上,有如下输出:

5:Attach Listener
4:Signal Dispatcher
3:Finalizer
2:Reference Handler
1:main

1.2 线程上下文切换

上面所说的多线程,避免不了的就是线程上下文的切换。
举个简单的例子:
你一边写着数据库操作的代码,同时你还要看看是否你的代码真的对数据库产生了你预期的影响。那么你就要不时地在代码和数据库查询之间来回切换。这来回切换当然是要花时间的。

多线程也一样,多个线程之间的切换肯定是要付出代价的(比如线程堆栈信息的记录和恢复等)。
所以,有些情况下,多线程可能不一定比单线程的效率好。
比如,你的需求就是执行1+2+3这么简单的任务,你非要弄两个线程来计算。那一般情况下肯定是单线程比较快了。
这就好比于你非要用hadoop或Spark来计算1+1一样的效率了……

所以,不要为了使用多线程而使用多线程。

1.3 线程死锁

对于死锁的概念,大家肯定都有些认识了。
比如操作系统原理中所讲的“哲学家进餐”等经典的死锁示例,虽然这个例子是讲的进程死锁,但是原理都是一样的。
原因大致都是由于多个线程之间争用资源导致的。

下面这段示例程序,一定会发生死锁:

public class MyDeadLockDemo {
    private static String RESOURCE_A = "RESOURCE_A";
    private static String RESOURCE_B = "RESOURCE_B";

    public void playDemo() {
        new Thread(() -> {
            synchronized (RESOURCE_A) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (RESOURCE_B) {
                    System.out.println("Thread A");
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (RESOURCE_B) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (RESOURCE_A) {
                    System.out.println("Thread A");
                }
            }
        }).start();

    }

    public static void main(String[] args) {
        new MyDeadLockDemo().playDemo();
    }
}

该示例中,第一个线程锁住了资源A等待资源B,第二个线程锁住了资源B等待资源A。所以一定会发生死锁。

1.4 CAS

请参看这位道友的文章:http://www.cnblogs.com/lintong/p/4373723.html

2 java中和多线程有关的知识

2.1 synchronized

相比于下面要提到的volatile,synchronized更加的重量级。平时我们在一些源码看到的更多的往往是synchronized。

用synchronized关键字修饰的方法或者代码块,当一个线程要访问该代码块的时候必须先获得锁,当然在访问结束(包括异常)的时候应该适当的释放锁。

** 同时也只能有一个线程处于代码块或方法中(排他性) ,进入代码块或方法的线程需要获取对象的监视器 **。

ArrayList和Vector的size方法

当然,获取锁和释放锁都是有性能开销的。据说,在java1.6中进行了优化。

2.2 volatile

在一定的角度来说volatile是轻量级的synchronized。
它能保证当一个线程修改一个共享变量时,另外一个线程能读到这个已经被修改过的值。也就是说能确保所有线程看到这个变量的值是一致的。具体的底层原理就牵扯到CPU和主存以及两者之间的cache了。

另外,volatile不会像synchronized那样需要线程上下文的切换。所以它比synchronized更加低成本。

总之,任何对volatile修饰的成员变量的访问都都直接从共享内存中获取,对他的更改必须直接写回主存中。

2.3 java中的锁

在java中,任何对象都可以作为锁来使用。

  • 非静态的同步方法,锁是当前对象(this)
  • 静态同步方法,锁是当前类(.class或者说是Class类的实例)
  • 同步的方法块,锁是synchronized关键字所包围的对象

通常所说的一个线程获得了锁,意味着:

  • 获得锁,获得了执行被同步代码的资格
  • 释放锁,可以向获取同一个锁的线程发送“消息”(notify,notifyAll)
  • 释放锁,该线程对本地变量的修改都会同步到主存中

2.3.1 偏向锁

同一个锁,往往会被同一个线程多次获得。

如果程序没有竞争,则取消之前已经取得锁的线程的同步操作。
也就是说,若某一锁被线程获取后,进入“偏向模式”,当线程再次请求这个锁时,就无需再进行相关的同步操作了。
从而省去了同步所需的时间,等到竞争出现了,则锁退出偏向模式。

相关的JVM参数

//偏向锁启动的延时
-XX:BiasedLockingStartupDelay=0
//关闭偏向锁
-XX:-UseBiasedLocking=false

2.3.2 轻量级锁

这里所说的轻量级锁是相对于传统的锁而言的。
轻量级锁一般是是线程交替执行同步块。

如果存在同一时间访问同一锁的情况,就会导致轻量级升级为重量级锁。

2.3.3 重量级锁

重量级锁一般而言指的是传统的java锁的实现方式。

2.3.4 锁的对比

**注:以下表格来自《Java并发编程的艺术》一书 **

几种锁的对比

2.4 java线程通信

虽然java里也有消息传递这种第三方的线程通信模型,但我们常说的java线程通信还是指的是共享内存模型。

假设两个线程A和B通信,至少有如下方式:

  • A修改volatile变量,B读这个volatile变量
  • A修改volatile变量,B用CAS更新这个volatile变量
  • A用CAS更新一个volatile变量,B用CAS更新这个volatile变量
  • A用CAS更新一个volatile变量,B读这个volatile变量

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,689评论 0 11
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,166评论 11 349
  • 参考链接:http://smallbug-vip.iteye.com/blog/2275743 在多线程开发的过程...
    时之令阅读 1,544评论 2 5
  • Java-Review-Note——4.多线程 标签: JavaStudy PS:本来是分开三篇的,后来想想还是整...
    coder_pig阅读 1,624评论 2 17
  • 相信大家都知道Android默认的横屏模式是右向的,即Title对着手机的右侧。 实现的方式有几种,这里不做重点介...
    Yeewoe阅读 933评论 0 0