对象及变量的并发访问

本文详细探讨了Java中对象及变量的并发访问问题,重点关注`synchronized`关键字的使用,包括同步方法、同步代码块、锁重入、异常释放锁以及非继承性。同时,文章还分析了`volatile`关键字的作用和限制,以及在并发编程中如何确保线程安全。
摘要由CSDN通过智能技术生成

title: 对象及变量的并发访问
categories: Java多线程编程核心技术
tags: 多线程
time: 2019-04-21 08:36:04

synchronized同步方法

“非线程安全”会在多个线程中的同一对象的实例变量在并发访问时发生,产生的后果就是“脏读”,即读到的数据其实是被更改过的。
“线程安全”就是以获得的实例变量的值是经过同步处理过的,不会出现“脏读”现象。

方法内的变量为线程安全

“非线程安全”问题存在与“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,即线程安全。

实例变量非线程安全

如果多个线程访问一个对象的实例变量,则有可能产生“非线程安全”问题。

public class HasSelfPrivateNum {

    private int num = 0;


    public void addNum(String name) {
        try {
            if ("a".equals(name)) {
                num = 100;
                System.out.println("a set over");
                Thread.sleep(200);
            } else {
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(name + " num=" + num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
        Test1 test1 = new Test1(hasSelfPrivateNum);
        Test2 test2 = new Test2(hasSelfPrivateNum);
        test1.start();
        test2.start();
    }

}

class Test1 extends Thread {
    private HasSelfPrivateNum hasSelfPrivateNum;
    Test1(HasSelfPrivateNum hasSelfPrivateNum) {
        this.hasSelfPrivateNum = hasSelfPrivateNum;
    }
    @Override
    public void run() {
        super.run();
        hasSelfPrivateNum.addNum("a");
    }
}

class Test2 extends Thread {
    private HasSelfPrivateNum hasSelfPrivateNum;
    Test2(HasSelfPrivateNum hasSelfPrivateNum) {
        this.hasSelfPrivateNum = hasSelfPrivateNum;
    }
    @Override
    public void run() {
        super.run();
        hasSelfPrivateNum.addNum("b");
    }
}

运行结果:

a set over
b set over
b num=200
a num=200

分析:Test1和Test2两个线程同时访问一个没有同步的方法addNum,则出现了线程不安全的问题。解决方式只需要给addNum方法前加synchronized关键字。

加锁后的执行效果:

a set over
a num=100
b set over
b num=200

多个对象多个锁

 public static void main(String[] args) {
        HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
        HasSelfPrivateNum hasSelfPrivateNum2 = new HasSelfPrivateNum();
        Test1 test1 = new Test1(hasSelfPrivateNum);
        Test2 test2 = new Test2(hasSelfPrivateNum2);
        test1.start();
        test2.start();
    }

运行结果:

a set over
b set over
b num=200
a num=100

分析:两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是异步的方式运行的。
关键字synchronized取得的锁是对象锁,而不是把一段代码或方法作为锁,所以在上面的示例中哪个线程先执行带synchrozined关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。但如果多个线程访问多个对象,例如上面示例中创建了2个HasSelfPrivateNum.java对象,所以就会产生2个锁,会异步执行。

synchronized方法与锁对象

验证synchronized与对象的关系

public class SynchronizedMethodLock {

    public void methodA() {
        try {
            System.out.println(Thread.currentThread().getName() + " run");
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SynchronizedMethodLock obj = new SynchronizedMethodLock();
        ThreadA threadA = new ThreadA(obj);
        threadA.setName("A");
        ThreadB threadB = new ThreadB(obj);
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }

}

class ThreadA extends Thread {
    private SynchronizedMethodLock lock;

    public ThreadA(SynchronizedMethodLock obj) {
        this.lock = obj;
    }
    @Override
    public void run() {
        super.run();
        lock.methodA();
    }
}

class ThreadB extends Thread {
    private SynchronizedMethodLock lock;

    public ThreadB(SynchronizedMethodLock obj) {
        this.lock = obj;
    }
    @Override
    public void run() {
        super.run();
        lock.methodA();
    }
}

运行结果:

A run
B run
end
end

分析:给methodA不加锁的情况下,是异步执行的;

 synchronized public void methodA() {
        try {
            System.out.println(Thread.currentThread().getName() + " run");
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

给methodA加锁后是同步执行的,即排队执行。

A run
end
B run
end

结论:
1.调用synchronized声明的方法是排队运行的;
2.共享资源的读写需要同步化,否则就没有同步的必要。

同步方法和普通方法执行验证

public class SynchronizedMethodLock2 {

    synchronized public void methodA() {
        try {
            System.out.println(Thread.currentThread().getName() + " run:" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void methodB() {
        try {
            System.out.println(Thread.currentThread().getName() + " run:" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SynchronizedMethodLock2 obj = new SynchronizedMethodLock2();
        ThreadC threadC = new ThreadC(obj);
        threadC.setName("C");
        ThreadD threadD = new ThreadD(obj);
        threadD.setName("D");
        threadC.start();
        threadD.start();
    }

}

class ThreadC extends Thread {
    private SynchronizedMethodLock2 lock;

    public ThreadC(SynchronizedMethodLock2 obj) {
        this.lock = obj;
    }
    @Override
    public void run() {
        super.run();
        lock.methodA();
    }
}

class ThreadD extends Thread {
    private SynchronizedMethodLock2 lock;

    public ThreadD(SynchronizedMethodLock2 obj) {
        this.lock = obj;
    }
    @Override
    public void ru
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值