二、Java多线程synchronized锁机制详解 - 简单实用

一、Synchronized概念

其实每个java对象都是可以实现同步的内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。这也是一个互斥锁,同一时间只有一个线程能够获得锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。

对象锁和类锁:对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。每个类只有一个class对象,但是却可以有多个实例对象,所以不同对象实例的对象锁是互不干扰。但是每个类只有一个类锁。其实类锁只是一个抽象的概念,其实只需要理解锁定实例方法和静态方法的区别。

注:Synchronized锁的是一个对象。

二、Synchronized对象锁

1、一个对象多个线程

package com.xiangping.thread.demo;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;

/**
 * 
 * @author 向平
 * @version $Id: Test.java, v 0.1 2018年8月10日 上午10:52:45 向平 Exp $
 */
class XPThreadFactory implements ThreadFactory {
  /**
   * 原子操作保证每个线程都有唯一的
   */
  private static final AtomicInteger threadCount = new AtomicInteger(1);

  private final AtomicInteger mThreadNum = new AtomicInteger(1);

  private final String threadName;

  private final boolean daemonThread;

  private final ThreadGroup threadGroup;

  public XPThreadFactory() {
    this("XPThreadFactory-" + threadCount.getAndIncrement(), false);
  }

  public XPThreadFactory(String prefix) {
    this(prefix, false);
  }


  public XPThreadFactory(String threadName, boolean isDaemon) {
    this.threadName = StringUtils.isNotEmpty(threadName) ? threadName+"thread-":"thread-";
    daemonThread = isDaemon;
    SecurityManager s = System.getSecurityManager();
    threadGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
  }

  @Override
  public Thread newThread(Runnable runnable) {
    String name = threadName + mThreadNum.getAndIncrement();
    Thread ret = new Thread(threadGroup, runnable, name, 0);
    ret.setDaemon(daemonThread);
    return ret;
  }
}


class XPSynchronized {
  public synchronized void getSum() {
    System.out.println(Thread.currentThread().getName() + ":getSum start");
    int sum = 0;
    for (int i = 0; i < 10; i++) {
      sum += i;
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {}
    }
    System.out.println(Thread.currentThread().getName() + ":getSum end:sum=" + sum);
  }
}


public class Test {
  public static void main(String[] args) {
    XPSynchronized task = new XPSynchronized();
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
            new XPThreadFactory("xiangping", false), new AbortPolicy());
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.getSum();
      }
    });

    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.getSum();
      }
    });
  }
}

执行结果:

xiangpingthread-1:getSum start
xiangpingthread-1:getSum end:sum=45
xiangpingthread-2:getSum start
xiangpingthread-2:getSum end:sum=45

结果分析:我们从执行结果可以看出,是线程1执行完成以后,线程2才执行的,代表线程1获取了锁住了对象(this对象,隐式第一个参数),当线程2去执行的时候,只能等待线程1释放对象才能执行。

2.多个对象,多个线程:(上面有的重复代码就不展示了,看关键的代码)

public class Test {
  public static void main(String[] args) {
    XPSynchronized task = new XPSynchronized();
    XPSynchronized task1 = new XPSynchronized();
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
            new XPThreadFactory("xiangping", false), new AbortPolicy());
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.getSum();
      }
    });

    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task1.getSum();
      }
    });
  }
}

执行结果:

xiangpingthread-1:getSum start
xiangpingthread-2:getSum start
xiangpingthread-1:getSum end:sum=45
xiangpingthread-2:getSum end:sum=45

结果分析:synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,其他线程都只能呈等待状态。但是这有个前提:既然锁叫做对象锁,那么势必和对象相关,所以多个线程访问的必须是同一个对象。

三、synchronized锁重入

class XPSynchronizedLock {
  public synchronized void method1() {
    System.out.println("XPSynchronizedLock.method1()");
    method2();
  }

  public synchronized void method2() {
    System.out.println("XPSynchronizedLock.method2()");
    method3();
  }

  public synchronized void method3() {
    System.out.println("XPSynchronizedLock.method3()");
    method4();
  }

  public synchronized void method4() {
    System.out.println("XPSynchronizedLock.method3()");
  }
}


public class Test {
  public static void main(String[] args) {
    XPSynchronizedLock task = new XPSynchronizedLock();
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
            new XPThreadFactory("xiangping", false), new AbortPolicy());
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.method1();
      }
    });
  }
}

执行结果:

XPSynchronizedLock.method1()
XPSynchronizedLock.method2()
XPSynchronizedLock.method3()
XPSynchronizedLock.method3()

结果分析:对象本身是可以再次获取自己的内部锁的,这就叫做“锁重入的机制”。支持在父子类继承。

四、任务异常当前线程会释放锁
 

class XPSynchronizedException {
  public synchronized void except(){
      try{
          System.out.println(Thread.currentThread().getName()+"start");
          long l = Integer.MAX_VALUE;
          while (true){
              long lo = 10 / l;
              l--;
          }
      } catch (Exception e){
        e.printStackTrace();
      }
  }
}


public class Test {
  public static void main(String[] args) {
    XPSynchronizedException task = new XPSynchronizedException();
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
            new XPThreadFactory("xiangping", false), new AbortPolicy());
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.except();
      }
    });
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.except();
      }
    });
  }
}

执行结果:

xiangpingthread-1start
java.lang.ArithmeticException: / by zero
xiangpingthread-2start
	at com.xiangping.thread.demo.XPSynchronizedException.except(Test.java:78)
	at com.xiangping.thread.demo.Test$1.run(Test.java:98)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
	at com.xiangping.thread.demo.XPSynchronizedException.except(Test.java:78)
	at com.xiangping.thread.demo.Test$2.run(Test.java:104)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

结果分析:只有等到第一个线程抛出异常后,第二个线程才可以执行

五、Synchronized同步代码块

class XPSynchronizedBlock {
  public void block() {
    for (int i = 0; i < 10; i++) {
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
      }
      System.out.println(Thread.currentThread().getName()+"++++非synchronized修饰  " + i);
    }
    System.out.println();
    synchronized (this) {
      for (int i = 0; i < 10; i++) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        System.out.println(Thread.currentThread().getName()+"---synchronized修饰  " + i);
      }
    }
  }
}


public class Test {
  public static void main(String[] args) {
    XPSynchronizedBlock task = new XPSynchronizedBlock();
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
            new XPThreadFactory("xiangping", false), new AbortPolicy());
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.block();
      }
    });
    threadPool.execute(new Runnable() {
      @Override
      public void run() {
        task.block();
      }
    });
  }
}

执行结果:

xiangpingthread-2++++非synchronized修饰  0
xiangpingthread-1++++非synchronized修饰  0
xiangpingthread-1++++非synchronized修饰  1
xiangpingthread-2++++非synchronized修饰  1
xiangpingthread-1++++非synchronized修饰  2
xiangpingthread-2++++非synchronized修饰  2
xiangpingthread-2++++非synchronized修饰  3
xiangpingthread-1++++非synchronized修饰  3
xiangpingthread-2++++非synchronized修饰  4
xiangpingthread-1++++非synchronized修饰  4
xiangpingthread-2++++非synchronized修饰  5
xiangpingthread-1++++非synchronized修饰  5
xiangpingthread-2++++非synchronized修饰  6
xiangpingthread-1++++非synchronized修饰  6
xiangpingthread-2++++非synchronized修饰  7
xiangpingthread-1++++非synchronized修饰  7
xiangpingthread-1++++非synchronized修饰  8
xiangpingthread-2++++非synchronized修饰  8
xiangpingthread-2++++非synchronized修饰  9
xiangpingthread-1++++非synchronized修饰  9


xiangpingthread-1---synchronized修饰  0
xiangpingthread-1---synchronized修饰  1
xiangpingthread-1---synchronized修饰  2
xiangpingthread-1---synchronized修饰  3
xiangpingthread-1---synchronized修饰  4
xiangpingthread-1---synchronized修饰  5
xiangpingthread-1---synchronized修饰  6
xiangpingthread-1---synchronized修饰  7
xiangpingthread-1---synchronized修饰  8
xiangpingthread-1---synchronized修饰  9
xiangpingthread-2---synchronized修饰  0
xiangpingthread-2---synchronized修饰  1
xiangpingthread-2---synchronized修饰  2
xiangpingthread-2---synchronized修饰  3
xiangpingthread-2---synchronized修饰  4
xiangpingthread-2---synchronized修饰  5
xiangpingthread-2---synchronized修饰  6
xiangpingthread-2---synchronized修饰  7
xiangpingthread-2---synchronized修饰  8
xiangpingthread-2---synchronized修饰  9

结果分析:当第一个线程访问对象的synchronized代码块的时候,第二个线程依然可以访问对象方法中其余非synchronized块的部分,当第一个线程进入对象的synchronized代码块的时候,第二个线程如果要访问这段synchronized块,那么访问将会被阻塞。

猜你喜欢

转载自blog.csdn.net/xp_zyl/article/details/81565524