Java多线程 多线程是什么意思( 二 )


Ad-hoc 线程封闭非常脆弱 , 应该尽量避免使用 。
栈封闭
栈封闭是我们编程当中遇到的最多的线程封闭 。
什么是栈封闭呢?
简单的说就是局部变量 。
多个线程访问一个方法 , 此方法中的局部变量都会被拷贝一份到线程栈中 。所以局部变量是不被多个线程所共享的 , 也就不会出现并发问题 。所以能用局部变量就别用全局的变量 , 全局变量容易引起并发问题 。
2.6.2 无状态的类
没有任何成员变量的类 , 就叫无状态的类 , 这种类一定是线程安全的 。
无状态就是一次操作 , 不能保存数据 。无状态对象(Stateless Bean) , 就是没有实例变量的对象.不能保存数据 , 是不变类 。
2.6.3 让类不可变
让状态不可变 , 两种方式:
1、 加 final 关键字 , 对于一个类 , 所有的成员变量应该是私有的 , 同样的只要有可能 , 所有的成员变量应该加上 final 关键字 , 但是加上 final , 要注意如果成员变量又是一个对象时 , 这个对象所对应的类也要是不可变 , 才能保证整个类是不可变的 。
2、根本就不提供任何可供修改成员变量的地方 , 同时成员变量也不作为方法的返回值 。
2.6.4 volatile
并不能保证类的线程安全性 , 只能保证类的可见性 , 最适合一个线程写 , 多个线程读的情景 。
2.6.5 加锁和CAS
我们最常使用的保证线程安全的手段 , 使用 synchronized 关键字 , 使用显式锁 , 使用各种原子变量 , 修改数据时使用 CAS 机制等等 。
2.6.6 安全的发布
类中持有的成员变量 , 如果是基本类型 , 发布出去 , 并没有关系 , 因为发布出去的其实是这个变量的一个副本 。
但是如果类中持有的成员变量是对象的引用 , 如果这个成员对象不是线程安全的 , 通过 get 等方法发布出去 , 会造成这个成员对象本身持有的数据在多线程下不正确的修改 , 从而造成整个类线程不安全的问题 。
2.6.7 ThreadLocal
ThreadLocal 是实现线程封闭的最好方法 。
ThreadLocal 内部维护了一个 Map , Map 的 key 是每个线程的名称 , 而 Map 的值就是我们要封闭的对象 。每个线程中的对象都对应着 Map 中一个值 , 也就是 ThreadLocal 利用 Map 实现了对象的线程封闭 。
2.7 实现线程安全的方式
2.7.1 synchronized(自动锁 , 锁的创建和释放都是自动的)
synchronized(同一个锁){ //可能会发生线程冲突问题 }
锁的释放是在synchronized同步代码执行完毕后自动释放 。
同步的前提:
1 , 必须要有两个或者两个以上的线程  , 如果小于2个线程 , 则没有用 , 且还会消耗性能(获取锁 , 释放锁)
2 , 必须是多个线程使用同一个锁
弊端:多个线程需要判断锁 , 较为消耗资源、抢锁的资源 。
2.7.2 lock 手动锁(手动指定锁的创建和释放)
可以视为synchronized的增强版 , 提供了更灵活的功能 。该接口提供了限时锁等待、锁中断、锁尝试等功能 。synchronized实现的同步代码块 , 它的锁是自动加的 , 且当执行完同步代码块或者抛出异常后 , 锁的释放也是自动的 。
2.7.3 volatile关键字
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后 , 那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性 , 即一个线程修改了某个变量的值 , 这新值对其他线程来说是立即可见的 。
2)禁止进行指令重排序 。
synchronized、volatile和Lock之间的区别
synochronizd和volatile关键字区别:
1)volatile关键字解决的是变量在多个线程之间的可见性;而sychronized关键字解决的是多个线程之间访问共享资源的同步性 。
tip: final关键字也能实现可见性:被final修饰的字段在构造器中一旦初始化完成 , 并且构造器没有把 “this”的引用传递出去(this引用逃逸是一件很危险的事情 , 其它线程有可能通过这个引用访问到了"初始化一半"的对象) , 那在其他线程中就能看见final;