• Java多线程synchronized同步


    非线程安全问题

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

    如果多个线程共同访问1个对象中的实例变量,则可能线程不安全。下面以实例说明

     1 public class HasSelfNum {
     2     private int num = 0;
     3     public void add(String name) {
     4         try {
     5             if (name.equals("a")) {
     6                 num = 100;
     7                 System.out.println("a over");
     8                 Thread.sleep(1000);
     9             } else {
    10                 num = 200;
    11                 System.out.println("b over");
    12             }
    13             System.out.println(name+" "+num);
    14         } catch (InterruptedException e) {
    15             // TODO Auto-generated catch block
    16             e.printStackTrace();
    17         }
    18     }
    19 }
     1 public class ThreadA extends Thread{
     2     private HasSelfNum hasSelfNum;
     3     
     4     public ThreadA(HasSelfNum hasSelfNum) {
     5         this.hasSelfNum = hasSelfNum;
     6     }
     7     
     8     @Override
     9     public void run() {
    10         super.run();
    11         hasSelfNum.add("a");
    12     }
    13 }
     1 public class ThreadB extends Thread{
     2     private HasSelfNum hasSelfNum;
     3     
     4     public ThreadB(HasSelfNum hasSelfNum) {
     5         this.hasSelfNum = hasSelfNum;
     6     }
     7     
     8     @Override
     9     public void run() {
    10         super.run();
    11         hasSelfNum.add("b");
    12     }
    13 }
     1 public class Main {
     2     public static void main(String[] args) {
     3         HasSelfNum hasSelfNum = new HasSelfNum();
     4         ThreadA threadA = new ThreadA(hasSelfNum);
     5         ThreadB threadB = new ThreadB(hasSelfNum);
     6         
     7         threadA.start();
     8         threadB.start();
     9     }
    10 }

    Result

    1 a over
    2 b over
    3 b 200
    4 a 200
    View Code
    非线程安全!
    如何处理?
    在add方法上加上关键字 synchronized
    synchronized public void add(String name)
    Result
    1 a over
    2 a 100
    3 b over
    4 b 200
    View Code

    线程安全,同步访问add()方法

    如果是多个对象的情况?

    将上面的Main.java进行修改

     1 public class Main {
     2     public static void main(String[] args) {
     3         HasSelfNum hasSelfNum = new HasSelfNum();
     4         HasSelfNum hasSelfNum2 = new HasSelfNum(); //两个对象
     5         
     6         ThreadA threadA = new ThreadA(hasSelfNum);
     7         ThreadB threadB = new ThreadB(hasSelfNum2);
     8         
     9         threadA.start();
    10         threadB.start();
    11     }
    12 }

    Result

    1 a over
    2 b over
    3 b 200
    4 a 100
    View Code
    两个线程分别访问同一个类的不同实例的相同同步方法,产生了两个锁,运行的结果是异步的。
    由此可以看出,关键字synchronized取得的锁是对象锁

     

    若类中既有synchronized类型方法又有非synchronized类型方法

     1 public class MyObject {
     2     synchronized public void methodA(){
     3         try {
     4             System.out.println(Thread.currentThread().getName()+" begin");
     5             Thread.sleep(1000);
     6             System.out.println(Thread.currentThread().getName()+" end");
     7         } catch (InterruptedException e) {
     8             // TODO Auto-generated catch block
     9             e.printStackTrace();
    10         }
    11     }
    12     
    13     public void methodB(){
    14         try {
    15             System.out.println(Thread.currentThread().getName()+" begin");
    16             Thread.sleep(1000);
    17             System.out.println(Thread.currentThread().getName()+" end");
    18         } catch (InterruptedException e) {
    19             // TODO Auto-generated catch block
    20             e.printStackTrace();
    21         }
    22     }
    23         
    24 }
     1 public class ThreadA extends Thread{
     2     MyObject myObject;
     3     public ThreadA(MyObject    myObject) {
     4         this.myObject = myObject;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         super.run();
    10         myObject.methodA();
    11     }
    12 }
     1 public class ThreadB extends Thread{
     2     MyObject myObject;
     3     public ThreadB(MyObject    myObject) {
     4         this.myObject = myObject;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         super.run();
    10         myObject.methodB();
    11     }
    12 }
     1 public class Main {
     2     public static void main(String[] args) {
     3         MyObject myObject = new MyObject();
     4         
     5         ThreadA threadA = new ThreadA(myObject);
     6         ThreadB threadB = new ThreadB(myObject);
     7         threadA.setName("A");
     8         threadB.setName("B");
     9         
    10         threadA.start();
    11         threadB.start();
    12     }
    13 }

    Result

    1 A begin
    2 B begin
    3 B end
    4 A end
    View Code
    线程A持有myObject对象锁,但线程B仍可以异步调用非synchronized类型方法
    当在methodB方法前也加上synchronized关键字时
    Result
    1 A begin
    2 A end
    3 B begin
    4 B end
    View Code

    线程A、B以同步的方式执行对象中的两方法

    synchronized同步代码块

    synchronized修饰的方法里有个耗时很长的代码时,效率是很低的。同步代码块分为:
    1.synchronized(this)
    在方法中不在同步代码块中的代码异步执行,而在其中的代码同步执行
    同个对象中的synchronized(this)使用的对象监视器是同一个,也就是说,当一个线程访问某个synchronized(this)代码块里面的方法时,其他线程访问其他synchronized(this)块里面的方法是会被阻塞的。另外synchronized(this)使用的监视器和syncheronized 修饰方法一致是当前对象,也会阻塞synchronized修饰的方法。
    2.synchronized(obj) 非this
    多个线程同时访问时只能阻塞synchronized(obj)的代码块。
    同时如果锁是同一个myObject对象,还是会阻塞的
     1 public class MyObject {
     2     synchronized public void methodA() {
     3         try {
     4             System.out.println(Thread.currentThread().getName() + " begin");
     5             Thread.sleep(1000);
     6             System.out.println(Thread.currentThread().getName() + " end");
     7         } catch (InterruptedException e) {
     8             // TODO Auto-generated catch block
     9             e.printStackTrace();
    10         }
    11     }
    12     public void methodB(MyObject myObject) {
    13         synchronized (myObject) {
    14             try {
    15                 System.out.println(Thread.currentThread().getName() + " begin");
    16                 Thread.sleep(1000);
    17                 System.out.println(Thread.currentThread().getName() + " end");
    18             } catch (InterruptedException e) {
    19                 // TODO Auto-generated catch block
    20                 e.printStackTrace();
    21             }
    22         }
    23     }
    24 }
     1 public class ThreadA extends Thread{
     2     MyObject myObject;
     3     public ThreadA(MyObject    myObject) {
     4         this.myObject = myObject;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         super.run();
    10         myObject.methodA();
    11     }
    12 }
     1 public class ThreadB extends Thread{
     2     MyObject myObject;
     3     public ThreadB(MyObject    myObject) {
     4         this.myObject = myObject;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         super.run();
    10         myObject.methodB(myObject);
    11     }
    12 }
     1 public class Main {
     2     public static void main(String[] args) {
     3         MyObject myObject = new MyObject();
     4         
     5         ThreadA threadA = new ThreadA(myObject);
     6         ThreadB threadB = new ThreadB(myObject);
     7         threadA.setName("A");
     8         threadB.setName("B");
     9         
    10         threadB.start();
    11         threadA.start();
    12     }
    13 }

    Result

    1 B begin
    2 B end
    3 A begin
    4 A end

    《Java多线程编程核心技术》学习

  • 相关阅读:
    Thrift在微服务中的使用
    MySQL 必知必会
    IDEA 内使用 git
    分布式锁
    LeetCode 图
    LeetCode 位运算
    LeetCode 数组
    LeetCode 字符串
    LeetCode 哈希表
    LeetCode 栈和队列
  • 原文地址:https://www.cnblogs.com/zhaoww/p/5827352.html
Copyright © 2020-2023  润新知