zoukankan      html  css  js  c++  java
  • Java线程--线程的同步与锁

    一、同步问题的提出

    线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

    例如:两个线程ThreadA、ThreadB都操作同一个对象,并修改Foo对象上的数据。

     1 package ThreadSynchronization;
     2 
     3 public class Foo {
     4     private int x=100;
     5     
     6     public int getX() {
     7         return this.x;
     8     }
     9     
    10     public int fix(int y) {
    11         x=x-y;
    12         return x;
    13     }
    14 }
     1 package ThreadSynchronization;
     2 
     3 public class MyRunnable implements Runnable{
     4 
     5     //私有成员
     6     private Foo foo=new Foo();
     7     
     8     //测试
     9     public static void main(String[] args) {
    10         MyRunnable r=new MyRunnable();
    11         //创建两个线程
    12         Thread ta=new Thread(r,"Thread-A");
    13         Thread tb=new Thread(r,"Thread-B");
    14         
    15         ta.start();
    16         tb.start();
    17     }
    18     
    19     @Override
    20     public void run() {
    21         for(int i=0;i<3;i++) {
    22             System.out.println(Thread.currentThread().getName()+"==========");
    23             this.fix(30);
    24             try {
    25                 Thread.sleep(1);
    26             } catch (Exception e) {
    27                 e.printStackTrace();
    28             }
    29             System.out.println(Thread.currentThread().getName()+":当前foo对象的x值="+foo.getX());
    30         }
    31     }
    32     
    33     //自己类里面也定义了一个调用fix方法的方法
    34     public int fix(int y) {
    35         return foo.fix(y);
    36     }
    37 }

    运行结果:

      运行结果不唯一,而且可以看出两个线程执行过程中有的一次循环没执行完就休眠,然后另一个线程就开始执行了,所以结果不合理。

      要想保持结果合理,要对Foo的访问加以限制,每次只能一个线程访问。

    在具体的Java代码中需要完成一下两个操作:

    把竞争访问的资源类Foo变量x标识为private;

    同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。

    二、同步和锁定

    1.锁的原理

    Java中每个对象 都有一个内置锁

      当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

    当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。

      一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。

    释放锁是指持锁线程退出了synchronized同步方法或代码块。


    关于锁和同步,有以下几个要点:

    1)只能同步方法,而不能同步变量和类;

    2)每个对象只有一个锁;当提到同步的时,应该清楚在什么上同步?也就是说,在哪个对象上同步?

    3)不必同步类中的所有方法,类可以同时拥有同步和非同步方法。

    4)如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。

    5)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。 

     6)线程睡眠时,它所持的任何锁都不会释放。 

    7)线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。 

    8)同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。 

    9)在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:

    1 public int fix(int y){
    2  synchronized(this){
    3   x=x-y;
    4  }
    5 }

    this是当前对象

    三、静态方法同步

    要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。

    例如:

    1 public static synchronized int setName(String name){
    2  xxx.name=name;
    3 }

    等价于

    1 public static int setName(String name){
    2  synchronized(xxx.class){
    3   xxx.name=name;
    4  }
    5 }

    四、如果线程不能获得锁会怎样

    如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的一种池中,必须在那里等待,直到其锁被释放,该线程再次变为可运行或运行为止。

    当考虑阻塞时,一定要注意哪个对象正被用于锁定:

    1.调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象锁,线程间彼此互补干预。

    2.调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。

    3.静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。

    4.对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。

  • 相关阅读:
    将python list数据结果存为html
    英文分词对比nltk vs spacy
    dict读取字典的第一个值-python
    分批查询数据库数据存入文件
    jupyter lab中显示gym的游戏动图
    模型融合
    读取csv中某列转为数字,顺序不变
    tensorflow和pytorch中的自定义网络层理解
    【小白刷题之路Day31】leetcode768. 最多能完成排序的块 II (动态规划、单调栈(没弄懂))
    【小白刷题之路Day31】leetcode769. 最多能完成排序的块(滑动窗口法、特殊使用、极致优化)
  • 原文地址:https://www.cnblogs.com/xjs1874704478/p/10744532.html
Copyright © 2011-2022 走看看