Java多线程编程核心技术笔记:第2章 对象及变量的并发访问

1.sychronized同步方法

    1.1方法内的变量为线程安全

         “非线程安全”问题存在于"实例变量"中,如果方法内部的私有变量,则不存在"非线程安全"问题,所得结果也就是"线程安全"的了。

        

package sychronizedDemo;

public class HasSelfPrivateNum2 {
    public void addI(String username){
        int num=0;
        if(username.equals("a")) {
            num = 100;
            System.out.println("a set over!");

        }else{
            num=200;
            System.out.println("b set over");
        }
        System.out.println(username+"num="+num);
    }

}
class ThreadA1 extends Thread{
    private HasSelfPrivateNum2 numRef;
    public ThreadA1(HasSelfPrivateNum numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}
class ThreadB1 extends Thread{
    private HasSelfPrivateNum2 numRef;
    public ThreadB1(HasSelfPrivateNum2 numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }
}
class Run1{
    public static void main(String[] args) {
        HasSelfPrivateNum2 numRef=new HasSelfPrivateNum2();
        ThreadA1 athread=new ThreadA1(numRef);
        athread.start();
        ThreadB1 bthread=new ThreadB1(numRef);
        bthread.start();

    }
}

结果如下:


可见,方法中的变量不存在线程安全问题,永远都是线程安全的。这是方法内部的变量是私有的特性造成的。

    2.2实例变量非线程安全

          如果多个线程共同访问1个对象中的实例变量,则有可能出现"非线程安全"问题。

package sychronizedDemo;

public class HasSelfPrivateNum {
    private int num=0;
    public void addI(String username){
        if(username.equals("a")) {
            num = 100;
            System.out.println("a set over!");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else{
            num=200;
            System.out.println("b set over");
        }
        System.out.println(username+"num="+num);
    }

}
class ThreadA extends Thread{
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}
class ThreadB extends Thread{
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }
}
class Run{
    public static void main(String[] args) {
        HasSelfPrivateNum numRef=new HasSelfPrivateNum();
        ThreadA athread=new ThreadA(numRef);
        athread.start();
        ThreadB bthread=new ThreadB(numRef);
        bthread.start();

    }
}

 结果为:


    本实验是两个线程同时访问一个没有同步的方法,如果两个线程同时访问对象中的实例变量,则有可能出现"非线程安全"问题。解决方法:只需要在public void addI(String username)方法前加入关键字sychronized即可。

    2.3多个对象多个锁

          例2.1、2.2介绍的都是多个对象一个锁的情况,那么多个对象多个锁会出现什么情况呢?

将上例中创建两个HasSelfPrivateNum()对象,代码如下:

    

public class HasSelfPrivateNum {
    private int num=0;
    synchronized public void addI(String username){
        if(username.equals("a")) {
            num = 100;
            System.out.println("a set over!");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else{
            num=200;
            System.out.println("b set over");
        }
        System.out.println(username+" num="+num);
    }

}
class ThreadA extends Thread{
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}
class ThreadB extends Thread{
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef){
        this.numRef=numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }
}
class Run{
    public static void main(String[] args) {
        HasSelfPrivateNum numRef1=new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2=new HasSelfPrivateNum();
        ThreadA athread=new ThreadA(numRef1);
        athread.start();
        ThreadB bthread=new ThreadB(numRef2);
        bthread.start();

    }
}

结果为:


   发现运行的结果是异步的交叉结果,明明使用了synchronized关键字为什么会这样呢?

      关键字synchronized取得的锁都是对象锁,而不是把一段代码或者方法当成锁;

所以在实例中,因为是两个对象,所以调用这两个对象创建了相对于持有该方法的对象创建了锁LOCK,所以会产生两个锁,因此没有产生同步的效果。

    2.4synchronized方法和锁对象

2.4.1 排队执行  

public class MyObject{
    public void methodA(){
        try {
            System.out.println("begin methodA threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end "+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ThreadA extends Thread{

    private MyObject object;
    public ThreadA(MyObject object){
        super();
        this.object=object;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
class ThreadB extends Thread{
    private MyObject object;
    public ThreadB(MyObject object){
        super();
        this.object=object;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
class Run{
    public static void main(String[] args) {
        MyObject object=new MyObject();
        ThreadA threadA=new ThreadA(object);
        ThreadB threadB=new ThreadB(object);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }

}
 

结果是:

修改MyObject代码为:

public class MyObject{
    synchronized public void methodA(){
        try {
            System.out.println("begin methodA threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end "+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
得到结果:
 
通过对比,通过调用关键字synchronized声明的方法一定是排队运行的。
但是只有共享资源的读写需要进行同步化,如果不是共享资源,那么根本没有同步的必要。

2.4.2相同对象不同方法的Lock

    (1)如果我们一个对象中有两个方法,其中一个对象上锁,另一个对象不上锁。那么同时访问这两个对象,会出现同步效果吗?实验:

    

public class MyObject{
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
     public void methodB(){
        try {
            System.out.println("begin method threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end "+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ThreadA extends Thread{

    private MyObject object;
    public ThreadA(MyObject object){
        super();
        this.object=object;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}
class ThreadB extends Thread{
    private MyObject object;
    public ThreadB(MyObject object){
        super();
        this.object=object;
    }

    @Override
    public void run() {
        super.run();
        object.methodB();
    }
}
class Run{
    public static void main(String[] args) {
        MyObject object=new MyObject();
        ThreadA threadA=new ThreadA(object);
        ThreadB threadB=new ThreadB(object);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }

}

     结果:


我们发现并没有产生同步的效果。 虽然线程A先持有Object对象的锁。但是线程B完全可以通过异步调用synchronized类型的方法。

修改methodB()方法前加上synchronized关键字:

 synchronized public void methodB(){
        try {
            System.out.println("begin method threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end "+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

结果是:


结论:1)A线程先持有Object对象的Object锁,B线程可以以异步的方式调用Object对象中的非synchronized类型的方法。

         2) A线程现持有Object对象的锁,B线程同时调用Object对象中的同步锁类型的方法则需要等待。

2.5 脏读

public class PublicVar {
    public String username="a";
    public String password="aa";
    synchronized public void setValue(String username,String password){
        try {
            this.username=username;
            Thread.sleep(1000);
            this.password=password;
            System.out.println("setValue method thread name="+Thread.currentThread().getName()+" username="+username+"password="+password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getValue(){
        System.out.println("getValue method thread name="+Thread.currentThread().getName()+" username="+username+" passeord="+password);
    }
}
class ThreadA1 extends Thread{
    private PublicVar publicVar;
    public ThreadA1(PublicVar publicVar){
        this.publicVar=publicVar;
    }

    @Override
    public void run() {
        publicVar.setValue("B","BB");
    }
}
class Test{
    public static void main(String[] args) throws InterruptedException {
        PublicVar publicVar=new PublicVar();
        ThreadA1 threadA1=new ThreadA1(publicVar);
        threadA1.start();
        Thread.sleep(200);
        publicVar.getValue();
    }
}

结果:


    方法setValue()和getValue()依次执行,但是却发生了脏读现象。只要将getValue()方法上加入同步锁就可以了。

    因为setValue()拿到了对象锁,getValue()在运行时要等待setValue()释放对象锁才能执行。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值