Java并发程序基础

JMM 的关键技术点都是围绕着多线程的原子性可见性有序性来创建的。所以,咱们首先必须了解这些概念。java

    1.原子性(Atomicity)多线程

原子性是指一个操做是不可中断的。即便是在多个线程一块儿执行的时候,一个操做旦开始,就不会被其余线程干扰。并发

for example:ide

对于一个静态int类型变量,当有多个线程同时操做修改其值时,它的值无非那几个之一,可是对于long类型的,在32位操做系统就可能会出现,结果形成干扰。性能

    2.可见性(Visibility)优化

可见性是指当一个线程修改了某一个共享变量的值时,其余线程是否可以当即知道这个修改。spa

显然,对于串行程序来讲,可见性问题是不存在的。由于你在任何一个操做步骤中修改了某个操作系统

变量,在后续的步骤中读取这个变量的值时,读取的必定是修改后的新值。可是在多线程并发线程

的状况下,若是修改了,那么其余线程不必定能马上发现这个改动code

for example:

Thread Thread 2

r1=p; r6=p;

r2=r1.X; r6. X=3

r3=9

r4=r3. X;

r5=r1. X;

 

这里假设在初始时,p=q 而且 p. X=0。对于大部分编译器来讲,可能会对线程 1 进行向前替换的优化,也就是 r5=r1. X 这条指令会被直接替换成 rS=r2。因为它们都读取了 r1. X,又发生在同一个线程中,所以,编译器极可能认为第 2 次读取是彻底没有必要的。所以,上述指令可能会变成

Thread Thread 2

r1=p; r6=p;

r2=r1.X; r6. X=3

r3=9

r4=r3. X;

r5=r2;

 

如今思考这么一种场景。假设线程 2 中的 r6. X=3 发生在 r2=rl. X 和 r4=r3. X 之间,而编译器又打算重用 r2 来表示 rS,那么就有可能出现很是奇怪的现象。你看到的 r2 是 0, r4 是 3, 可是 r5 仍是 0。所以,若是从线程 1 代码的直观感受上看就是:p. X 的值从 0 变成了 3(由于 r4 是 3),接着又变成了 0(这是否是算一个很是怪异的问题呢?)。

    3.有序性(Ordering)

有序性问题的缘由是程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。下面来看一个简单的例子

Class Orderexample {
int a=0
Boolean flag falser public void writer () {
a=1
flag=true;
}
public void reader () {
if  (flag) {
int i= a +1;
...
}
}
}

 

假设线程 A 首先执行 writer 方法,接着线程 B 执行 reader 方法,若是发生指令重排,那么线程 B 在代码第 10 行时,不必定能看到 a 已经被赋值为 1 了。

 

分门别类的管理-线程组

在一个系统中,若是线程数量不少,并且功能分配比较明确,就能够将相同功能的线程放置在同一个线程组里。打个比方,若是你有一个苹果,你能够把它拿在手里,可是若是你有十个苹果,你最好还有一个篮子,不然不方便携带。对于多线程来讲,也是这个道理。想要轻松处理几十个甚至上百个线程,最好仍是将它们都装进对应的篮子里。

Threadgroup tg =new Threadgroup ("Printgroup");//定义一个线程组
Thread tl = new Thread (tg, new Threadgroupname (), "T1"); //将T1线程放入线程组
Thread t2 =new Thread (tg, new Threadgroupname (), " T2") 
t1.start(); 
t2.start();
System.out.println (tg.activeCount ());//得到活动线程的总数
tg.list () ;//打印全部的线程信息

 

守护线程(加在线程启动前)

/**
 * 守护线程
 */
public class DaemonThread {
    public static class Daemont extends Thread{
        @Override
        public void run() {
            while (true){
                System.out.println("live");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Daemont();
        //Daemont线程就成了守护线程了,在主线程休眠后退出,守护线程也随之退出,而且须要加在线程启动前
        //若是不加上的这个的话,在主线程退出后,Daemont线程还会一直执行下去,
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(5000);
    }
}

 

Volatile与synchronized

Volatile

Volatile能够看作是一个轻量级的synchronized,它能够在多线程并发的状况下保证变量的“可见性”,什么是可见性?

就是在一个线程的工做内存中修改了该变量的值,该变量的值当即能回显到主内存中,从而保证全部的线程看到这

个变量的值是一致的。因此在处理同步问题上它大显做用,并且它的开销比synchronized小、使用成本更低。

 

synchronized

synchronized叫作同步锁,是Lock的一个简化版本,因为是简化版本,那么性能确定是不如Lock的,不过它操做起来方便,

只须要在一个方法或把须要同步的代码块包装在它内部,那么这段代码就是同步的了,全部线程对这块区域的代码访问

必须先持有锁才能进入,不然则拦截在外面等待正在持有锁的线程处理完毕再获取锁进入,正由于它基于这种阻塞的策

略,因此它的性能不太好,可是因为操做上的优点,只须要简单的声明一下便可,并且被它声明的代码块也是具备操做的

原子性。

 

总结:关于Volatile关键字具备可见性,但不具备操做的原子性,而synchronized比volatile对资源 的消耗稍微大点,但能够保证变量操做的原子性,保证变量的一致性,最佳实践则是两者结合一块儿使用。

相关文章
相关标签/搜索