zoukankan      html  css  js  c++  java
  • 多线程-synchronized

    扎好马步:线程的状态

    先来两张图:


    线程状态

    线程状态转换

    各种状态一目了然,值得一提的是"blocked"这个状态:
    线程在Running的过程中可能会遇到阻塞(Blocked)情况

    1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
    2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
    3. 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。

    此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

    由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
    需要明确的几个问题:

    • synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果 再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
    • 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
    • 每个对象只有一个锁(lock)与之相关联。
    • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

    synchronized关键字的作用域有二种:

    1. 某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线 程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
    2. 某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

    synchronized 方法

    每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
    在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

    synchronized 方法的缺陷

    同步方法,这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象 P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加 了synchronized关键字的方法.同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
    ;若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

    synchronized 代码块

    除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象。
    这时锁就是对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:

    class Foo implements Runnable {
           private byte[] lock = new byte[0]; // 特殊的instance变量    
           Public void methodA() {      
             synchronized(lock) { //… }
           }
           //…..
    }

    注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

    synchronized 静态方法

    将synchronized作用于static 函数,示例代码如下:

    Class Foo {
      // 同步的static 函数
      public synchronized static void methodAAA()  {
      //….
      }
      public void methodBBB() {
           synchronized(Foo.class)   // class literal(类名称字面常量)
      }    
    }

    代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。

    可以推断:如果一个类中定义了一个synchronized 的 static 函数A,也定义了一个 synchronized 的 instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。B方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。

    文/DanieX(简书作者)
    原文链接:http://www.jianshu.com/p/ea9a482ece5f
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

    下面是另一篇博客,写得也不错。

    来自: http://zhangjunhd.blog.51cto.com/113473/70300/

    在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。

    1.对象的锁
    所有对象都自动含有单一的锁。
    JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。
    只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。
    每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。
    2.synchronized同步块
    2.1同步到单一对象锁
    当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。
    Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象 synchronized (this)),所以对它们的方法依然是互斥的。
    Resource1.java
    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
     
    public class Resource1 {
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public static void main(String[] args) {
           final Resource1 rs = new Resource1();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    Thread-1:not synchronized in g()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    2.2 同步到多个对象锁
    Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,这些同步块处在不同的方法中,并且是同步到三个不同的对象(synchronized (this),synchronized(syncObject1),synchronized (syncObject2)),所以对它们的方法中的临界资源访问是独立的。
    Resource2.java
    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
     
    public class Resource2 {
        private Object syncObject1 = new Object();
        private Object syncObject2 = new Object();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           synchronized (syncObject1) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           synchronized (syncObject2) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public static void main(String[] args) {
           final Resource2 rs = new Resource2();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    main:synchronized in h()
    Thread-1:not synchronized in g()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    3.Lock对象锁
    除了使用synchronized外,还可以使用Lock对象来创建临界区。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.java。
    Resource3.java
    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    public class Resource3 {
        private Lock lock = new ReentrantLock();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public static void main(String[] args) {
           final Resource3 rs = new Resource3();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    Thread-1:not synchronized in g()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Resource4.java
    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    public class Resource4 {
        private Lock lock1 = new ReentrantLock();
        private Lock lock2 = new ReentrantLock();
        private Lock lock3 = new ReentrantLock();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           lock1.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock1.unlock();
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           lock2.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock2.unlock();
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           lock3.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock3.unlock();
           }
        }
     
        public static void main(String[] args) {
           final Resource4 rs = new Resource4();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    main:synchronized in h()
    Thread-1:not synchronized in g()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    齊帥
  • 相关阅读:
    自定义组件要加@click方法
    绑定样式
    647. Palindromic Substrings
    215. Kth Largest Element in an Array
    448. Find All Numbers Disappeared in an Array
    287. Find the Duplicate Number
    283. Move Zeroes
    234. Palindrome Linked List
    202. Happy Number
    217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/qishuai/p/8724904.html
Copyright © 2011-2022 走看看