可见性&Lock

本文探讨了线程间通信中可见性问题,通过volatile关键字保证数据同步,以及线程内存模型中的堆栈交互。重点讲解了如何利用`volatile`解决多线程中flag的同步问题,并对比了可见性和原子同步的区别。涉及《Java并发编程实战》中的实例和概念。
摘要由CSDN通过智能技术生成

可见性

  • 线程与线程之间互相独立私有,线程与线程之间如何通信
    • 利用堆内存交换更新数据 达到线程间通信
    • 通信不及时,或者永远都不会更新,线程间不可见
  • 线程内存模型
    • 堆内存:所以线程公共可见
      • 栈内存的数据是从堆内存中读来的,读的时机 以及写的时机不受进程控制
    • 线程内存:
      • 从堆栈获取数据,拷贝副本,对副本进行操作,操作完更新到堆内存
  • 原因:多个线程在使用同一个资源的时候,会产生 某个线程修改资源后,其它线程不知道的情况
  • 解决:使用volatile 保证这个被修饰变量 被某个线程更新后,其它线程都知道

有两个线程:Thread1,Thread2;
Thread1由flag决定是否停止;
Thread2修改flag;
使用volatile修饰flag时,Thread2修改flag之后Thread1会知道修改后的flag;
如果没有volatile修饰,运行程序,Thread1可能无法停止,因为它知道的flag始终是true 。

public class test {
    //  volatile 保证这个被修饰变量 被某个线程更新后,其它线程能够知道
    volatile static boolean flag = true;

    public static void main(String[] args) {

        new Thread() {
            @Override
            public void run() {
                System.out.println("Thread 01 start");
                while (flag) {
//                    System.out.print("");
                }
                System.out.println("Thread 01 end");
            }
        }.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread() {
            @Override
            public void run() {
                System.out.println("Thread 02 start");
                flag = false;
                System.out.println("Thread 02 end");
            }
        }.start();
    }

}

原子同步,与可见性

  • 可见性可以解决 原子同步性吗?
    • 是不可以的
      • Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。但volatile不保证volatile变量的原子性。

Lock:

  • 隐式锁:不需要手动解锁、自动解锁
  • 显示锁:需要手动加锁,手动解锁
  • 上锁后一定要确保可以实现对资源的解锁操作

任务Task 有String属性,add() 方法——给String增添字符;
线程WorkThread implements Runnable,属性有Task、Lock、String;
重写run()方法,循环调用30次Task.add() 方法;
在主线程中创建两个WorkThread,在线程安全的情况下运行正确。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Task {
    String str = "";
    public void add(String value) {
        this.str += value;
        if (str.length() == 40) {
            throw new IndexOutOfBoundsException("测试异常!!!");
        }
    }
}
class WorkThread implements Runnable{
    // 创建一把锁
    static Lock lock = new ReentrantLock();

    String value;
    Task task;
    public WorkThread(String value, Task task) {
        this.value = value;
        this.task = task;
    }
    @Override
    public void run() {
        int count = 30;
//        System.out.println(task.str);
        while (count > 0) {
            count--;
            lock.lock();
            try {
				task.add(value);
				lock.unlock();
			} catch (IndexOutOfBoundsException e) {
				e.printStackTrace();
                lock.unlock();
			}
//            System.out.println(task.str);
        }
    }
}
public class TestLock {
    public static void main(String[] args) {
        Task task = new Task();
        WorkThread wt1 = new WorkThread("a", task);
        WorkThread wt2 = new WorkThread("b", task);

        Thread t1 = new Thread(wt1);
        Thread t2 = new Thread(wt2);

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(task.str);
        System.out.println(task.str.length());
    }
}

参考资料

使用线程的场景 - 线程并发安全的产生原因以及解决方法
volitale怎么保证可见性
《Java并发编程实战》Brian Goetz / Tim Peierls / Joshua Bloch / Joseph Bowbeer / David Holmes / Doug Lea

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值