关于Java中多线程并发的可见性和Lock

本文探讨了volatile关键字在Java多线程中的作用,解释了为何不加volatile会导致死锁问题,并通过实例演示了如何正确使用锁(ReentrantLock)确保线程安全。还强调了在异常处理中解锁的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于volatile关键字的使用:

public class Test {
    static boolean flag = true;
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println("thread1 begin");
                while(flag){

                }
                System.out.println("thread1 end");
            }
        }.start();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(){
            @Override
            public void run() {
                System.out.println("thread2 begin");
                flag = false;
                System.out.println("thread2 end");
            }
        }.start();
    }
}

在线程1执行的时候,控制台只能输出thread1 begin的语句,代表thread1线程已经开始运行,原因是我在thread1线程中设置了一个while死循环。在线程睡眠了几秒之后,thread2线程开始运行,控制台输出了thread2 begin的语句,随后执行将flag标识赋值为false,再输出thread2 end语句。

但是thread1线程并没有结束,可以看到控制台并没有输出thread1 end语句。

结果如下所示:

 分析原因:

每一个线程的栈内存都是独立的,而我们设置的flag是存储在堆内存当中的,当线程要操作堆内存当中的资源时,需要将其复制到栈内存中进行操作,然后将执行的结果再赋给堆内存中的资源。但是此例子当中flag虽然已经被赋值为false,而thread1线程中的死循环并没有停止。原因是线程与线程之间是互相独立私有的,只能通过以上方法(利用堆内存交换更新的数据)来达到线程之间的通信效果。但是这样的通信是不及时的,也可能永远都不会更新,可能会出现多个线程操作同一个堆内存当中的资源时,其中一个线程对资源进行修改之后,其他线程不知道的情况。因此我们需要给变量加上volatile关键字,这样的话,其中一个线程对资源进行修改之后,别的线程也能够知道。

修改后的代码以及控制台输出结果如下所示:

 

 关于多线程中lock的使用方法:

如下举例:

public class Test {
    public static void main(String[] args) {
        Action action = new Action();
        Thread1 thread1 = new Thread1(action,"d");
        Thread1 thread11 = new Thread1(action,"k");
        Thread t1 = new Thread(thread1);
        Thread t2 = new Thread(thread11);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(action.str);
        System.out.println(action.str.length());
    }
}
class Action{
    String str = "";
    public void add(String value){
        str += value;
    }
}
class Thread1 implements Runnable{
    Action action;
    String value;
    public Thread1(Action action, String value) {
        this.action = action;
        this.value = value;
    }
    @Override
    public void run() {
        int count = 0;
        while(count<30){
            action.add(value);
            count++;
        }
    }
}

由于没有给资源上锁,多个线程操作同一个资源,导致出现了线程不安全的情况,如下为控制台输出结果:

 将原有代码进行改进(给资源上锁):

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

public class Test {
    public static void main(String[] args) {
        Action action = new Action();
        Thread1 thread1 = new Thread1(action,"d");
        Thread1 thread11 = new Thread1(action,"k");
        Thread t1 = new Thread(thread1);
        Thread t2 = new Thread(thread11);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(action.str);
        System.out.println(action.str.length());
    }
}
class Action{
    String str = "";
    public void add(String value){
        str += value;
    }
}
class Thread1 implements Runnable{
    //构造一把锁
    static Lock lock = new ReentrantLock();(此处注意需要用static来修饰lock,只可以设置一把锁,多个线程来争抢一把锁的使用权)
    Action action;
    String value;
    public Thread1(Action action, String value) {
        this.action = action;
        this.value = value;
    }
    @Override
    public void run() {
        int count = 0;
        while(count<30){
            //对于资源的操作上锁
            lock.lock();
            action.add(value);
            //解锁
            lock.unlock();
            count++;
        }
    }
}

修改后的控制台输出情况如下:

再次将代码修改

经过修改后的情况:

 

 此时的代码出现了一个问题,就是在第一个拿到锁使用权的线程抛出错误后,中断了程序,从而导致锁一直被锁着,没有进行解锁的操作,导致其他的线程都没有任何办法进行对资源的操作。因此在使用lock对资源上锁的时候,一定要设置解锁,需要保证在任何情况下无论出现什么意外程序都会执行unlock()操作。

修改方法如下所示(将unlock()操作写入finally代码块中):

异常虽然被抛出,但是依然执行了解锁操作,没有影响程序的运行。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值