第2章 线程与锁-2.1 内存和互斥模型

1 简单粗暴

线程与锁模型其实是对底层硬件运行过程的形式化。这既是它的优点也是它的缺点。

2 内存和互斥模型

2.1 创建线程

java中,并发的基本单元是线程,线程之间通过共享内存进行通信。

多线程版本Hello World

public class HelloWorld{

    public static void main(String[] args){
        Thread myThread = new Thread(){
            public void run(){
                System.out.println("Hello from new thread");
            }
        };

        myThread.start();
        Thread.yield();
        System.out.println("Hello from main thread");

        try{
            // 等待myThread线程结束
            myThread.join();
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

Thread.yield( )方法
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。

join方法
阻塞所在线程,等调用它的线程执行完毕,再向下执行

第一把锁

public class Counting{
    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{
            public void run(){
                for(int x=0;x<1000; ++x){
                    counter.increment();
                }
            }
        }

        CountingThread t1= new CountingThread();
        CountingThread t2 = new CountingThread();

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.getCount());
    }
}

如果不对其进行加锁的话,最后的输出结果会在2000和小于2000之间。
如果把 自增的方法改为

public synchronized void increment(){++count;}

就能保证最后结果为2000。(但其中有一个诡异的bug)

诡异的内存

public class Puzzle{

    static boolean answerReady = false;
    static int answer = 0;
    static Thread t1 = new Thread(){
        public void run(){
            answer = 42;
            answerReady = true;
        }
    };

    static Thread t2 = new Thread(){
        public void run(){

            if(answerReady){
                System.out.println(answer);
            }else{
                System.out.println("No");
            }
        }
    };


    public static void main(String[] args) throws Exception{
        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }
}

运行结果
这里写图片描述
这里还有一种可能的结果就是输出为0 。

原因如下:

  • 编译器的静态优化可以打乱代码的执行顺序。
  • JVM的动态优化也会打乱代码的执行顺序。
  • 硬件可以通过乱序执行来提高性能。

因此,我们需要一个标准来明确告诉我们,可能发生的副作用,这就是内存模型。

内存可见性
java内存模型定义了何时一个线程对内存的修改对另一个线程可见。基本原则是:如果读线程和写线程不进行同步,就不能保证可见性。

除了上面提到的对象内置锁的同步方法,其他方法包括:开始一个线程并通过join检查线程是否已经终止;使用java.util.concurrent提供的工具包。

多把锁

用多把锁不仅会让运行速度变慢,还可能会导致死锁。

相关阅读:

内存模型: http://blog.csdn.net/suifeng3051/article/details/52611310
volatile和 synchronized区别 http://blog.csdn.net/suifeng3051/article/details/52611233
详解 volatile http://www.importnew.com/18126.html
双重检查 http://blog.csdn.net/dl88250/article/details/5439024
用内部类和双重检查实现单例 http://blog.csdn.net/android_freshman/article/details/51029031

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值