多线程问答

目录

java 程序中怎么保证多线程的运行安全?

ThreadLocal使用场景


java 程序中怎么保证多线程的运行安全?

参考网址:java 程序中怎么保证多线程的运行安全

线程的安全性问题体现在:原子性、可见性、有序性。

体现方面

说明

导致原因

解决方法

原子性

一个或者多个操作在CPU执行的过程中不被中断的特性

线程切换

JDK Atomic开头的原子类(非阻塞CAS算法)

synchronized

LOCK

可见性

一个线程对共享变量的修改,另外一个线程能够立刻看到

缓存

synchronized

volatile

LOCK

有序性

程序执行的顺序按照代码的先后顺序执行

编译优化

Happens-Before规则

Happens-Before规则

参考网址:java内存模型以及happens-before规则 - 简书

1. happens-before含义

        JMM(java 内存模型)可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。具体的定义为:

(1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。

(2)两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。

上面的(1)是JMM对程序员的承诺。从程序员的角度来说,可以这样理解happens-before关系:如果A happens-before B,那么Java内存模型将向程序员保证——A操作的结果将对B可见,且A的执行顺序排在B之前。注意,这只是Java内存模型向程序员做出的保证!

上面的(2)是JMM对编译器和处理器重排序的约束原则。正如前面所言,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。JMM这么做的原因是:程序员对于这两个操作是否真的被重排序并不关心,程序员关心的是程序执行时的语义不能被改变(即执行结果不能被改变)。因此,happens-before关系本质上和as-if-serial语义是一回事。

2.happens-before存在的意义

        重排序原则有编译器重排序和处理器重排序等,如果让程序员再去了解这些底层的实现以及具体规则,那么程序员的负担就太重了,严重影响了并发编程的效率。因此,JMM为程序员在上层提供了六条规则,这样我们就可以根据规则去推论跨线程的内存可见性问题,而不用再去理解底层重排序的规则。

规则

说明

程序顺序规则 一个线程中的每个操作,happens-before于该线程中的任意后续操作
监视器锁规则 对一个锁的解锁,happens-before于随后对这个锁的加锁。

volatile变量规则

对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
传递性规则 如果A happens-before B,且B happens-before C,那么A happens-before C。
start()规则 如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
join()规则 如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

线程中断规则

对线程interrupt()的调用 happen—before 发生于被中断线程的代码检测到中断时事件的发生。

对象终结规则

就是一个对象的初始化的完成(构造函数执行的结束) happens-before它的finalize()方法

ThreadLocal使用场景

参考网址:ThreadLocal作用、场景、原理 - 简书

数据库连接

public Connection initialValue() {
    return DriverManager.getConnection(DB_URL);
}

public static Connection getConnection() {  
    return connectionHolder.get();
}

Session管理

public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

多线程

public class Demo {
    static ThreadLocal<String> localVariable = new ThreadLocal<>();

    static void print(String str){
        System.out.println(str + ":" + localVariable.get());
    }

    public static void main(String[] args) {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("thread local variable");
                print("threadOne");
                System.out.println("threadOne remove after" + ":" + localVariable.get());
            }
        });

        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("thread local variable");
                print("threadTwo");
                System.out.println("threadTwo remove after" + ":" + localVariable.get());
            }
        });

        threadOne.start();
        threadTwo.start();
    }
}

运行结果:

threadTwo:thread local variable
threadTwo remove after:thread local variable
threadOne:thread local variable
threadOne remove after:thread local variable

发布了126 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/feiying0canglang/article/details/104293092