2.1使用synchronized同步方法

方法私有变量是线程安全的

线程是否安全的关键问题在于实例中的变量
如果变量是方法的私有变量,则是线程安全

package com.test;

import com.myObject.Object1;
import com.myThread.Thread1a;
import com.myThread.Thread1b;

public class Test1 {
    public static void main(String[] args) throws InterruptedException {

        // 测试1
        Object1 object1 = new Object1();
        Thread1a thread1a = new Thread1a(object1);
        thread1a.start();
        Thread1b thread1b = new Thread1b(object1);
        thread1b.start();

    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1a extends Thread {
    Object1 object1;

    public Thread1a(Object1 object1) {
        this.object1 = object1;
    }

    @Override
    public void run() {
        super.run();
        object1.printI(404);
    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1b extends Thread {
    Object1 object1;

    public Thread1b(Object1 object1) {
        this.object1 = object1;
    }

    @Override
    public void run() {
        super.run();
        object1.printI(500);
    }
}
package com.myObject;

public class Object1 {
    String msg;

    public void printI(int i) {
        if (i == 404) {
            msg = "找不到请求资源";
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else if (i == 500)
            msg = "服务器出错";
        System.out.println(i + ":" + msg);
    }

}

打印结果

500:服务器出错
404:服务器出错

若将Object1 的全局变量String msg放到printI(int i)方法内或者使用关键字synchronized,则是线程安全的
打印结果会一直为

500:服务器出错
404:找不到请求资源

synchronized取得的是对象锁

1)关键字synchronized取得的是对象的锁,而不是把一段代码块或者方法当做锁
2)哪个线程先执行带有synchronized关键字的方法,哪个线程就获得该对象的锁
3)没有获得对象锁的线程,能执行同一对象并且没有上对象锁的方法即没有synchronized关键字的方法

package com.test;

import com.myObject.Object2;
import com.myThread.Thread2a;
import com.myThread.Thread2b;

public class Test2 {
    public static void main(String[] args) throws InterruptedException {

        // 测试1
        Object2 object2 = new Object2();

        Thread2a thread2a = new Thread2a(object2);
        thread2a.setName("A");

        Thread2b thread2b = new Thread2b(object2);
        thread2b.setName("B");

        thread2a.start();
        thread2b.start();

    }
}
package com.myThread;

import com.myObject.Object2;

public class Thread2a extends Thread {
    Object2 object2;

    public Thread2a(Object2 object2) {
        this.object2 = object2;
    }

    @Override
    public void run() {
        super.run();
        object2.mothdA();
    }

}
package com.myThread;

import com.myObject.Object2;

public class Thread2b extends Thread {
    Object2 object2;

    public Thread2b(Object2 object2) {
        this.object2 = object2;
    }

    @Override
    public void run() {
        super.run();
        object2.mothdB();
    }

}
package com.myObject;

public class Object2 {

    synchronized public void mothdA() {
        System.out.println("begin mothdA");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
        System.out.println("end mothdA");
    }

    public void mothdB() {
        System.out.println("begin mothdB");
        System.out.println(Thread.currentThread().getName());
        System.out.println("end mothdB");
    }

}

打印结果

begin mothdA
begin mothdB
B
end mothdB
A
end mothdA

分析:mothdA是上了对象锁的,而mothdB没有,所以当别的线程访问同一个对象时,没有同步的方法都可以执行

若将mothdB加上关键字synchronized
打印结果

begin mothdA
A
end mothdA
begin mothdB
B
end mothdB

小结:
1)A线程先取得Object的Lock,则B线程还是可以异步调用Object中没有sychronized关键字的方法
2)A线程先取得Object的Lock,则B线程调用Object中任何sychronized关键字的方法都需要等待

脏读

既然A线程先取得Object的Lock,则B线程还是可以异步调用Object中没有sychronized关键字的方法,那么若对象的读取实例变量的方法没有使用synchronized,则有可能出现脏读

synchronized锁重入

1)Object对象的mothdA()和mothdB()都是带synchronized
2)ThreadA调用mothdA(),而mothdA()调用mothdB(),ThreadA还是能得到Object的锁

package com.test;

import com.myObject.Object3;

public class Test3 {
public static void main(String[] args) {
    Thread thread =new Thread(){
        @Override
        public void run() {
            super.run();
            Object3 object3 =new Object3();
            object3.mothdA();
        }
    };
    thread.start();
}
}
package com.myObject;

public class Object3 {

    synchronized public void mothdA() {
        System.out.println(" mothdA");
        mothdB();

    }

    synchronized public void mothdB() {
        System.out.println(" mothdB");
    }

}

打印结果

mothdA
mothdB

分析
可重入锁就是在某线程获得某个对象的锁,然后在释放改锁之前,想再次获取还是可以获取的。

注意:锁的重入也支持继承环境,如下伪代码

class A{
synchronized publish void mothdA(){
...
}
}
class B extends A{
synchronized publish void mothdB(){
...
this.mothdA();
}
}
class Test{
  main(){
      Thread thread =new Thread(){
          @Override
          public void run() {
              super.run();
              B b=new B();
              b.mothdB();
          }
      };
      thread.start();
      }
}

异常会释放锁

package com.myObject;

import java.io.IOException;

public class Object4 {
synchronized public void exceptionMothd() {
    if("a".equals(Thread.currentThread().getName())){
        try {
            System.out.println("Thread A");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        int i=1/0;
    }
    else {
        System.out.println("Thread B");
    }
}
}
package com.test;

import com.myObject.Object4;

public class Test4 {

    public static void main(String[] args) throws InterruptedException {
        final Object4 object4 = new Object4();
        Thread threadA = new Thread() {
            @Override
            public void run() {
                super.run();
                object4.exceptionMothd();
            }
        };
        Thread threadB = new Thread() {
            @Override
            public void run() {
                super.run();
                object4.exceptionMothd();
            }
        };
        threadA.setName("a");
        threadB.setName("b");
        threadA.start();
        Thread.sleep(3000);
        threadB.start();
    }
}

打印结果

Thread A
Exception in thread "a" Thread B
java.lang.ArithmeticException: / by zero
at com.myObject.Object4.exceptionMothd(Object4.java:15)
at com.test.Test4$1.run(Test4.java:13)

线程A出现了异常释放了锁,然后线程B获得锁进入方法,进行打印

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值