文档章节

线程安全-Lock锁&synchronized

石飞飞
 石飞飞
发布于 2017/04/19 13:51
字数 1041
阅读 5
收藏 0

 

  1. 线程安全概念
    • 当多个线程访问某一个类(对象或者方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或者方法)就是线程安全的。
  2. Lock接口
    • Lock接口位于java.util.concurrent.locks包中。
    • Lock比传统线程模型的synchronized更加的面相对向,与生活中锁差不多,锁本身也应该是一个对象。两个线程执行相同的代码片段要实现同步互斥的效果,他们必须使用同一个Lock对象。Lock锁是锁在要操作的资源的类的内部方法中的,而不是线程代码中的。
  3. 场景应用
    • 多个线程打印一不同字符串,为了能让每一个线程分别能打印完自己的字符串,我们该怎么做呢?
    • public class LockTest {
      
          //打印字符串业务逻辑
          static class Printer {
              public void print(String name) {
                  for (int i = 0; i < name.length(); i++) {
                      System.out.print(name.charAt(i));
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  System.out.println();
              }
          }
      
          private void init() {
              final Printer printer = new Printer();
              //启动线程一
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("111111");
                      }
                  }
              }).start();
      
              //启动线程二
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("222222");
                      }
                  }
              }).start();
          }
      
          static LockTest lockTest = new LockTest();
      
          public static void main(String[] args) {
              lockTest.init();
          }
      }

      打印结果:

      121221211212
      1
      21221211212

      212112211212
      2
      我们看到结果并不是我们想要的:111111和222222

    • 我们用传统的synchronized关键字解决,也就是在共享资源的方法或者代码块上加上关键字synchronized

    • public class LockTest {
      
          //打印字符串业务逻辑
          static class Printer {
              public void print(String name) {
      
                  synchronized (Printer.class) {
                      for (int i = 0; i < name.length(); i++) {
                          System.out.print(name.charAt(i));
                          try {
                              Thread.sleep(10);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  }
                  System.out.println();
              }
          }
      
          private void init() {
              final Printer printer = new Printer();
              //启动线程一
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("111111");
                      }
                  }
              }).start();
      
              //启动线程二
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("222222");
                      }
                  }
              }).start();
          }
      
          static LockTest lockTest = new LockTest();
      
          public static void main(String[] args) {
              lockTest.init();
          }
      }

      打印结果:

      111111
      222222
      111111
      222222
      111111
    • 使用Lock锁对象来解决这个问题
    • public class LockTest {
      
          //打印字符串业务逻辑
          static class Printer {
              Lock lock = new ReentrantLock();
      
              public void print(String name) {
                  //上锁
                  lock.lock();
                  try {
                      for (int i = 0; i < name.length(); i++) {
                          System.out.print(name.charAt(i));
                          try {
                              Thread.sleep(10);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  } finally {
                      //不论发生什么情况,都释放锁
                      lock.unlock();
                  }
                  System.out.println();
              }
          }
      
          private void init() {
              final Printer printer = new Printer();
              //启动线程一
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("111111");
                      }
                  }
              }).start();
      
              //启动线程二
              new Thread(new Runnable() {
                  public void run() {
                      while (true) {
                          printer.print("222222");
                      }
                  }
              }).start();
          }
      
          static LockTest lockTest = new LockTest();
      
          public static void main(String[] args) {
              lockTest.init();
          }
      }
      

      打印结果:

      111111
      222222
      111111
      111111
      222222
  4. 在使用Lock对象时,一定要主要,不论发生何种异常情况,一定要释放锁对象。
  5. 多个线程多个锁问题
    • 多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容
    • 实例:
    • /**
       * Created by shifeifei on 2017/4/29.
       * 多个线程多个锁对象
       */
      public class MultiThread {
      
          private int num = 0;
      
          public synchronized void printNum(String tag) {
              try {
                  if ("a".equals(tag)) {
                      num = 100;
                      System.out.println("tag a,set num over");
                      Thread.sleep(1000);
                  } else {
                      num = 200;
                      System.out.println("tag b,set num over");
                  }
      
                  System.out.println("tag " + tag + ",num = " + num);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      
          public static void main(String[] args) {
              final MultiThread m1 = new MultiThread();
              final MultiThread m2 = new MultiThread();
      
              new Thread(new Runnable() {
                  public void run() {
                      m1.printNum("a");
                  }
              }).start();
      
              new Thread(new Runnable() {
                  public void run() {
                      m2.printNum("b");
                  }
              }).start();
      
          }
      
      }
      
       
      • 打印结果:
      • tag a,set num over

        tag b,set num over

        tag b,num = 200

        tag a,num = 100

      • 问题分析:我看 MultiThread的两个对象 m1和m2 分别在两个线程中访问带有synchronized的方法printNum,synchronized表示当前对象的锁,意思是m1和m2分别有一把锁,所以此时看到了上述的打印结果,如果解决:

    • public class MultiThread {
      
          private static int num = 0;
      
          public static synchronized void printNum(String tag) {
              try {
                  if ("a".equals(tag)) {
                      num = 100;
                      System.out.println("tag a,set num over");
                      Thread.sleep(1000);
                  } else {
                      num = 200;
                      System.out.println("tag b,set num over");
                  }
      
                  System.out.println("tag " + tag + ",num = " + num);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      
          public static void main(String[] args) {
              final MultiThread m1 = new MultiThread();
              final MultiThread m2 = new MultiThread();
      
              new Thread(new Runnable() {
                  public void run() {
                      m1.printNum("a");
                  }
              }).start();
      
              new Thread(new Runnable() {
                  public void run() {
                      m2.printNum("b");
                  }
              }).start();
      
          }
      
      }
      

      打印结果:

      • tag a,set num over
        tag a,num = 100
        tag b,set num over
        tag b,num = 200

      • 我们的做法是给printNum()加了static关键字,此时表synchronized锁是类锁(锁是上在MultiThread上的),所以m1和m2访问方法时,访问的是同一把锁,此时线程同步。

© 著作权归作者所有

石飞飞
粉丝 2
博文 64
码字总数 39883
作品 0
朝阳
程序员
私信 提问
Java多线程学习(二)

非线程安全:在多个线程对同一个对象中的实例进行并发访问时发生,产生的后果即为脏读。 线程安全:获得的实例变量的值是经过同步处理的,不会出现脏读现象。 非线程安全问题存在于实例变量中...

kakayang2011
2016/02/29
76
0
java 线程安全问题

java线程安全的处理我知道的现在就2种 lock、synchronized 我想线程安全封装在我框架的底层。但是在开发中我始终有个疑问 比如我有这样一个对象 我现在需要查询 UserDao 类 我查询出来会执行...

SandKing
2014/09/03
880
11
【19】Java中的Locks

原文地址:Locks in Java 作者: Jakob Jenkov --- Lock是一个类似同步代码块(synchronized block)的线程同步机制。同步代码块而言,Lock可以做到更细粒度的控制。 Lock(或者其他高级同步...

秋雨霏霏
2017/10/26
51
0
21、Java并发性和多线程-Java中的锁

以下内容转自http://ifeve.com/locks/: 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂。因为锁(以及其它更高级的线程同步机制)是由synchronize...

easonjim
2017/06/16
0
0
【Java并发性和多线程】Java中的锁

本文为转载学习 原文链接:http://ifeve.com/locks/ 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂。因为锁(以及其它更高级的线程同步机制)是由...

heroShane
2014/02/02
110
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL 11.3 locking

rudi
今天
5
0
Mybatis Plus sql注入器

一、继承AbstractMethod /** * @author beth * @data 2019-10-23 20:39 */public class DeleteAllMethod extends AbstractMethod { @Override public MappedStatement injectMap......

一个yuanbeth
今天
15
1
一次写shell脚本的经历记录——特殊字符惹的祸

本文首发于微信公众号“我的小碗汤”,扫码文末二维码即可关注,欢迎一起交流! redis在容器化的过程中,涉及到纵向扩pod实例cpu、内存以及redis实例的maxmemory值,statefulset管理的pod需要...

码农实战
今天
4
0
为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

之前在阅读《阿里巴巴Java开发手册》时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下: 那么我们首先来用例子来看看在循环体中用 + 或者用 StringBuilder 进行字符串拼接的效率...

武培轩
今天
9
0
队列-链式(c/c++实现)

队列是在线性表功能稍作修改形成的,在生活中排队是不能插队的吧,先排队先得到对待,慢来得排在最后面,这样来就形成了”先进先出“的队列。作用就是通过伟大的程序员来实现算法解决现实生活...

白客C
今天
93
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部