并发-基础

解决什么问题

1.为了让代码运行得更快,单纯依靠更快的硬件已无法满足要求,我们需要利用多核,充分发掘应用程序并发的潜力。
2.并发优势:解决问题的速度往往比单核串行程序快得多。
3.缺点:数据一致性。共享数据缺乏原子操作,带来数据不同步的问题。

概念

并发:同一个时刻,应对多件事情的能力。
并行:同一个时刻,处理多件事情的能力。
例子:班级制作一批红花。老师收到任务后,让5位同学挺身而出,接受了这个制作任务(并发)。任务完成后,老师边听汇报边批改作业(并行)。

并发程序要求

及时性(分布式部署响应)
高效(慢请求不影响其他请求)
容错(两个任务不互相影响,串行就不行)
简单(多分模块)

并发模型

线程:虽有很多不足之处,但是是基础
函数式:消除可变状态,线程安全
clojuer之道
actor: 共享内存模型,基于消息传递
CSP通信顺序模型
CPU:数据级并行计算
Lambda架构:类似map reduce

简单粗暴:线程

线程与锁:简单,程序容易出错,难以维护(禁忌:直接调用底层的Thread)

互斥
竞态/死锁

共享变量

public static void main(String[] args) throws InterruptedException {
        class Counter{
            private int count = 0;
            public void increment( ) { count++; }
            public int getCount() { return count; }
        }
        final Counter counter = new Counter();
        class CountingThread extends Thread {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    counter.increment();
                }
            }
        }

        CountingThread t1 = new CountingThread();
        CountingThread t2 = new CountingThread();
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(counter.getCount());
    }

// 结果:程序跑出来的结果是不确定的,但至少大于10000,少于20000
// 原因:并发竞态,导致资源没写就被其他线程读到
// 解决方法:加锁,public synchronized void increment( ) { count++; }

乱序

static boolean answerReady = false;
    static int answer = 0;
    static Thread t1 = new Thread(){
        public void run() {
            answerReady = true;
            answer = 42;
        };
    };
    static Thread t2 = new Thread(){
        public void run() {
            if (answerReady) {
                System.out.println("answer is " + answer);
            } else {
                System.out.println("not found answer");
            }
        };
    };

    public static void main(String[] args) {
        try {
            t1.start();t2.start();
            t1.join();t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
// 结果:1.输出answer is  42;2.输出not found answer;3.输出answer is 0;
// 原因:并发乱序执行,1编译器静态优化打乱代码执行顺序。2JVM动态化也能打乱顺序。3硬件乱序执行来优化性能
// 解决:if (answerReady) { 
Thread.sleep(100);
        System.out.println("answer is " + answer);
    } 
// 针对此类优化显然有副作用,因此最好不这么写代码

多锁

public static void main(String[] args) {
        class Thread_test extends Thread{
            public void run() {
                while (true) {
                    System.out.println("线程"+this.getName()+"饿了找餐具");
                    // 做点事情
                    if(knife_key ){
                        knife_key = false;
                        if(dish_key ){
                            dish_key = false;

                            // 用餐
                            try {
                                System.out.println("线程"+this.getName()+"在吃饭");
                                Thread.sleep(new Random().nextInt(100));
                                System.out.println("线程"+this.getName()+"在吃饭");
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            // 做完开锁
                            knife_key = true;
                            dish_key = true;


                            // 休息
                            try {
                                System.out.println("线程"+this.getName()+"在休息");
                                Thread.sleep(new Random().nextInt(5000));
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            };
        };

    }
    try {
            Thread_test t1 = new Thread_test();t1.setName("客户:t1");
            Thread_test t2 = new Thread_test();t2.setName("客户:t2");
            Thread_test t3 = new Thread_test();t3.setName("客户:t3");
            Thread_test t4 = new Thread_test();t4.setName("客户:t4");
            t1.start();
            t2.start();
            t3.start();t4.start();
            t1.join();
            t2.join();
            t3.join();t4.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

// 结果:当客户进餐时间加长或者客户增多时,每个客户都处于寻找餐具的状态
// 原因:死锁
// 方法:避免长时间持有锁,有顺序的持有锁

全局锁

锁惟一且确定

外星方法

长时间占用资源进行耗时操作
可以数据保护性复制,采用监听器同步

如何避免

  • 对所有共享变量的访问都需要同步化
  • 按照全局顺序持有锁
  • 持有锁避免使用外星方法
  • 持有锁时间尽可能短

课后思考

  1. java内存模型是如何保证对象初始化是线程安全的?是否必须通过加锁才能在线程之间安全地公开对象?
  2. 了解反模式“双重检查锁模式(double-checked locking)”
  3. 我们进行调试时,应该如何增大重现死锁的几率?

本人拙见非标准答案

  1. JMM模型使得堆内存在线程之间是共享的。即java对象建立到消亡是存于堆内存中,完全可以直接在内存中操作,不需要线程之间同步化,即也不需要加锁
  2. 反模式的双上锁,第二层锁针对变量是否初始化上锁,即针对内存中的共享变量的直接检查,防止线程之间变量未共享,就获取了变
  3. 1.增加并发线程数量。2.延长锁定时间。3.超时同步数据。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值