1. Java多线程基本概念

多线程的优点

  • 资源利用率更好(等待IO的时间)
  • 程序设计在某些情况下更简单(一个线程对应一个任务)
  • 程序相应更快(不用实时去相应)

多线程的代价

  • 设计更复杂(访问共享数据的机制)
  • 上下文切换的开销
  • 增加资源消耗(每个线程本身占用内存资源)

并发编程模型

并发工作者

传入的作业会被分配到不同的工作者上。

并行工作者
  • 优点:理解简单,可以通过增加工作者提高系统的并行度
  • 缺点:共享状态复杂、工作者无状态、任务的顺序不确定
流水线模式

每个工作者只负责作业的部分工作,当完成了这部分工作时的工作者会将作业转发给下一个工作者

流水线工作者
  • 优点:无需共享状态、有状态的工作者、更好的硬件整合,合理的工作顺序
  • 缺点:作业的执行分布在多个工作者上,追踪某个作业到底被什么代码执行困难。
  1. 函数式并行

Same-threading

Same-threading是由多个单线程系统组成,每个单线程系统之间不共享数据。

几种线程系统之间的比较

并行和并发

并发:应用同时处理多个任务,任务的开始不需要等另外一个任务结束。

Concurrency

并行:应用将一个任务分成多个小的子任务,每个子任务实例都是用一个CPU进行处理。

Parallelism

竞争条件(Race Condition)和关键区域(Critical Sections)

线程对关键区域的数据进行竞争,竞争的结果影响执行关键区域的结果

当多个线程对关键区域里面的相同资源进行操作时,会产生问题。其实只有多个线程进行写操作的时候才会产生问题。

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞争条件(race condition)
导致竞争条件发生的代码区称作临界区(critical section)

解决竞争条件的方法:

  1. synchronized
  2. lock
  3. atomic variable
    在某些情况下可以尝试分解同步块的作用范围
public class TwoSums {
    
    private int sum1 = 0;
    private int sum2 = 0;

    private Integer sum1Lock = new Integer(1);
    private Integer sum2Lock = new Integer(2);

    public void add(int val1, int val2){
        synchronized(this.sum1Lock){
            this.sum1 += val1;   
        }
        synchronized(this.sum2Lock){
            this.sum2 += val2;
        }
    }
}

线程安全和共享资源

线程安全(thread safe)

当多个线程同时调用的时候,代码是安全的。线程安全的代码不包含竞争条件。竞争条件只在多个线程更新共同资源的时候发生。

共享资源的安全性(shared resources)

  • 局部变量:存储在每个线程的栈当中,是线程安全的。
public void someMethod(){
    long threadSafeInt = 0;
    threadSafeInt++;
}
  • 局部对象引用:引用本身不共享,但是引用的对象是放在公共区域(堆)中的。如果对象不逃出创造它的方法,那么它也是线程安全的。
  public void someMethod(){
    LocalObject localObject = new LocalObject();
    localObject.callMethod();
    method2(localObject);
  }

  public void method2(LocalObject localObject){
    localObject.setValue("value");
  }
  • 成员变量:成员变量随着对象存储在堆中,如果两个线程调用同一对象的一个方法来更新这个成员变量,那么这个方法不是线程安全的。
public class NotThreadSafe{
    StringBuilder builder = new StringBuilder();

    public add(String text){
        this.builder.append(text);
    }
}

public class MyRunnable implements Runnable{
  NotThreadSafe instance = null;

  public MyRunnable(NotThreadSafe instance){
    this.instance = instance;
  }

  public void run(){
    this.instance.add("some text");
  }

  public static void main(String[] args) {
    NotThreadSafe sharedInstance = new NotThreadSafe();
    new Thread(new MyRunnable(sharedInstance)).start();
    new Thread(new MyRunnable(sharedInstance)).start();
  } 
}

如果两个线程调用add()方法的时候使用不同的实例对象,那么久不会产生竞争条件。

new Thread(new MyRunnable(new NotThreadSafe())).start();
new Thread(new MyRunnable(new NotThreadSafe())).start();

线程控制逃逸规则--判断对某些资源的访问是线程安全的

如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

即使对象本身是线程安全的,如果该对象中包含的其他资源(文件,数据库连接),整个应用也有可能不是线程安全的。

区分某个线程控制的对象是资源本身,还是仅仅到某个资源的应用很重要。

线程安全及不可变性

我们可以通过把共享的资源设为不可变的,使线程之间的共享资源永远不能发生改变,因此是线程安全的。

public class ImmutableValue{

  private int value = 0;

  public ImmutableValue(int value){
    this.value = value;
  }

  public int getValue(){
    return this.value;
  }

  public ImmutableValue add(int valueToAdd){
     return new ImmutableValue(this.value + valueToAdd);
   }
  
}

但是即使对象是不可变的,但是指向该对象的引用仍然可以是线程不安全的。在使用不可变特性来保证线程安全的时候,特别需要注意。

public class Calculator{
  private ImmutableValue currentValue = null;

  public ImmutableValue getValue(){
    return currentValue;
  }

  public void setValue(ImmutableValue newValue){
    this.currentValue = newValue;
  }

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

推荐阅读更多精彩内容