多线程与高并发4
1、volatile
import java.util.concurrent.TimeUnit;
public class T01 {
// volatile使得线程可见
volatile boolean running =true;
void m(){
System.out.println("m start");
while (running){
}
System.out.println("m end");
}
public static void main(String[] args) {
T01 t = new T01();
new Thread(t::m,"t1").start();
try{
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
t.running = false;
}
}
这段代码使用volatile和不使用volatile有明显区别
使用volatile:
不使用volatile:
####测试new Thread启动一个线程。调用了m方法。睡了1秒后。如果普通变量没有被volatile修饰。
将把running值从内存读到t1线程工作区。运行过程中无须每次都去读堆内存。所以值被修改是感知不到。必须等while循环结束。
如果running值被volatile修饰。会强制所有线程去堆内存读取running值。则会被感知到false跳出循环
####volatile作用:
1、保证线程可见性。堆内存也就是共享内存。当我们的线程去访问这个值的时候。会将此值copy一份。不需要检查有没有新值。这也是线程的不可见性。
而volatile则会强制读取堆内存变化值。一个线程改变。另外线程也能看到。
2、禁止指令重新排序
并行执行cpu指令。提高效率
1.1、禁止指令重排:dcl单例
单例就是指jvm内存里永远只有某一个类的一个实例
不需要new很多对象
饿汉模式和懒汉模式
饿汉。就是进入这个类。就给你实例化一个对象
public class Mgr01 {
// 单例模式饿汉(先上来就new一个对象)
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01(){
}
public static Mgr01 getInstance(){
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m2);
}
}
当我每次进来调用getInstance方法就初始化一次对象。其实这里需要判断INSTANCE是否为null再返回。是null的时候再返回,不要初始化多个对象
因此被我改写成这样。
import org.springframework.util.ObjectUtils;
public class Mgr01 {
// 单例模式饿汉(先上来就new一个对象)
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01(){
}
public static Mgr01 getInstance(){
if (ObjectUtils.isEmpty(INSTANCE)){
return INSTANCE;
}
return null;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m2);
}
}
懒汉式单例
public class Mgr03 {
// 懒汉式单例
private static Mgr03 INSTANCE;
private Mgr03(){
}
public static Mgr03 getInstance(){
if(INSTANCE == null){
try {
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i=0;i<100;i++){
new Thread (() -> {
System.out.println(Mgr03.getInstance().hashCode());
}).start();
}
}
}
懒汉式会出现线程不安全问题
其实我们本意不想让他返回新的对象了。因为对象为空就创建一个新对象。
所以这里在getInstance方法处增加synchronized 能保证线程安全问题?
public class Mgr04 {
// 懒汉式线程安全问题
private static Mgr04 INSTANCE;
private Mgr04(){
}
public synchronized static Mgr04 getInstance(){
if(INSTANCE == null){
try {
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Mgr04();
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i=0;i<100;i++){
new Thread (() -> {
System.out.println(Mgr04.getInstance().hashCode());
}).start();
}
}
}
结果是可以
懒汉式同步代码块实现
public class Mgr05 {
// 懒汉式同步代码块
private static Mgr05 INSTANCE;
private Mgr05(){
}
public static Mgr05 getInstance(){
if (INSTANCE == null){
synchronized (Mgr05.class){
try {
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Mgr05();
}
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i = 0;i<100;i++){
new Thread(() -> {
System.out.println(Mgr05.getInstance().hashCode());
}).start();
}
}
}
那他这个怎么去控制保证线程安全问题
答案是synchronized同步方法 + volatile线程可见
或者直接用synchronized同步方法,不使用同步代码块
public class Mgr06 {
// synchronized + volatile 解决懒汉式单例线程安全问题
private static volatile Mgr06 INSTANCE;
private Mgr06(){
}
public synchronized static Mgr06 getInstance(){
if (INSTANCE == null){
try{
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for(int i = 0 ;i<1000;i++){
new Thread(() -> {
System.out.println(Mgr06.getInstance().hashCode());
}).start();
}
}
}