精华内容
下载资源
问答
  • synchronized 和Lock

    2020-12-14 16:37:41
    synchronized 和Lock synchronized是java内置关键字,在jvm层面,Lock是个java类; synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁; synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;...

    synchronized 和Lock

    1. synchronized是java内置关键字,在jvm层面,Lock是个java类;
    2. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
    3. synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
    4. synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度
      总结:在项目中涉及到生成消费时,用到了synchronized,并使用了wait 、notify方法,并可以设置休眠时间。
      synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
        1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
        2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
        3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
        4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
      synchronized (this)之后,被锁住的代码块是同步的,但同时非同步块的方法可以被调用,同时可以更改this属性的值;如果把 this 替换成字符串或者数字,效果是一样的。只要是同一把锁,则会同步代码块。应该是同一把锁的原因,不管谁调用,只要锁不变,效果就是同步的
      特定唤醒:
      synchronized(o) {
      o.wait(); //wait状态
      }
      在其他线程中o.notify(),就可以唤醒在o上wait的线程。
    展开全文
  • synchronized和Lock

    2017-09-24 13:45:41
    同步的实现当然是采用锁了,java中使用锁的两个基本工具是 synchronized 和Lock

    A一般首先想到的同步synchronized

    // 未同步的方法
    public void test() {}
    // 同步的方法
    pubilc synchronized void test() {}
    
    synchronized 也可以用在一个代码块上,看
    
    public void test() {
         synchronized(obj) {
              System.out.println("===");
         }
    }

    synchronized 用在方法和代码块上有什么区别呢?

    synchronized 用在方法签名上(以test为例),当某个线程调用此方法时,会获取该实例的对象锁,方法未结束之前,其他线程只能去等待。当这个方法执行完时,才会释放对象锁。其他线程才有机会去抢占这把锁,去执行方法test,但是发生这一切的基础应当是所有线程使用的同一个对象实例,才能实现互斥的现象。否则synchronized关键字将失去意义。

    (但是如果该方法为类方法,即其修饰符为static,那么synchronized 意味着某个调用此方法的线程当前会拥有该类的锁,只要该线程持续在当前方法内运行,其他线程依然无法获得方法的使用权!)

    synchronized 用在代码块的使用方式:synchronized(obj){//todo code here}

    当线程运行到该代码块内,就会拥有obj对象的对象锁,如果多个线程共享同一个Object对象,那么此时就会形成互斥!特别的,当obj == this时,表示当前调用该方法的实例对象。即

    public void test() {

    synchronized(this) {
    // todo your code
    }

    }

    此时,其效果等同于
    public synchronized void test() {
    // todo your code
    }

    使用synchronized代码块,可以只对需要同步的代码进行同步,这样可以大大的提高效率。

    小结:
    使用synchronized 代码块相比方法有两点优势:
    1、可以只对需要同步的使用
    2、与wait()/notify()/nitifyAll()一起使用时,比较方便


    wait() 与notify()/notifyAll()

    这三个方法都是Object的方法,并不是线程的方法!
    wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会!

    notify(): 该方法会唤醒因为调用对象的wait()而等待的线程,其实就是对对象锁的唤醒,从而使得wait()的线程可以有机会获取对象锁。调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码。需要注意的是,wait()和notify()必须在synchronized代码块中调用。

    notifyAll()则是唤醒所有等待的线程。

    为了说明这一点,举例如下:
    两个线程依次打印”A”“B”,总共打印10次。

    public class Consumer implements Runnable {

     [@Override](https://my.oschina.net/u/1162528)
     public synchronized void run() {
            // TODO Auto-generated method stub
            int count = 10;
            while(count > 0) {
                 synchronized (Test. obj) {
    
                     System. out.print( "B");
                     count --;
                     Test. obj.notify(); // 主动释放对象锁
    
                      try {
                           Test. obj.wait();
    
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
    
           }
     }
    

    }

    public class Produce implements Runnable {

     [@Override](https://my.oschina.net/u/1162528)
     public void run() {
            // TODO Auto-generated method stub
            int count = 10;
            while(count > 0) {
                 synchronized (Test. obj) {
    
                      //System.out.print("count = " + count);
                     System. out.print( "A");
                     count --;
                     Test. obj.notify();
    
                      try {
                           Test. obj.wait();
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
    
           }
    
     }
    

    }

    测试类如下:

    public class Test {

     public static final Object obj = new Object();
    
     public static void main(String[] args) {
    
            new Thread( new Produce()).start();
            new Thread( new Consumer()).start();
    
     }
    

    }

    这里使用static obj作为锁的对象,当线程Produce启动时(假如Produce首先获得锁,则Consumer会等待),打印“A”后,会先主动释放锁,然后阻塞自己。Consumer获得对象锁,打印“B”,然后释放锁,阻塞自己,那么Produce又会获得锁,然后…一直循环下去,直到count = 0.这样,使用Synchronized和wait()以及notify()就可以达到线程同步的目的。


    除了wait()和notify()协作完成线程同步之外,使用Lock也可以完成同样的目的。

    ReentrantLock 与synchronized有相同的并发性和内存语义,还包含了中断锁等候和定时锁等候,意味着线程A如果先获得了对象obj的锁,那么线程B可以在等待指定时间内依然无法获取锁,那么就会自动放弃该锁。

    但是由于synchronized是在JVM层面实现的,因此系统可以监控锁的释放与否,而ReentrantLock使用代码实现的,系统无法自动释放锁,需要在代码中finally子句中显式释放锁lock.unlock();

    同样的例子,使用lock 如何实现呢?

    public class Consumer implements Runnable {

     private Lock lock;
     public Consumer(Lock lock) {
            this. lock = lock;
     }
     [@Override](https://my.oschina.net/u/1162528)
     public void run() {
            // TODO Auto-generated method stub
            int count = 10;
            while( count > 0 ) {
                 try {
                      lock.lock();
                     count --;
                     System. out.print( "B");
                } finally {
                      lock.unlock(); //主动释放锁
                      try {
                           Thread. sleep(91L);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
           }
    
     }
    

    }

    public class Producer implements Runnable{

     private Lock lock;
     public Producer(Lock lock) {
            this. lock = lock;
     }
     [@Override](https://my.oschina.net/u/1162528)
     public void run() {
            // TODO Auto-generated method stub
            int count = 10;
            while (count > 0) {
                 try {
                      lock.lock();
                     count --;
                     System. out.print( "A");
                } finally {
                      lock.unlock();
                      try {
                           Thread. sleep(90L);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
           }
     }
    

    }

    调用代码:

    public class Test {

     public static void main(String[] args) {
           Lock lock = new ReentrantLock();
    
           Consumer consumer = new Consumer(lock);
           Producer producer = new Producer(lock);
    
            new Thread(consumer).start();
            new Thread( producer).start();
    
     }
    

    }

    使用建议:

    在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock是个不错的方案。

    展开全文
  • Synchronized和Lock

    2018-09-27 20:16:41
    Synchronized和Lock的线程状态 让我们从Thread dump说起,在我们使用jstack将打印thread stack的时候,如果采用的是Synchronized进行并发控制的话会看到如下的日志: 如果采用的是ReentrantLock,会看到如下的...

    两者的线程状态

    让我们从Thread dump说起,在我们使用jstack将打印thread stack的时候,如果采用的是Synchronized进行并发控制的话会看到如下的日志:
    在这里插入图片描述
    如果采用的是ReentrantLock,会看到如下的日志:
    在这里插入图片描述
    我们会发现使用Synchronize内置锁使线程阻塞的状态是BLOCKED状态,而使用Lock显示锁使线程阻塞的状态是WAITING状态

    BLOCKED 和 WAITING

    从操作系统内核来看,线程都是在阻塞态,没有区别。区别在于由谁唤醒,是操作系统,还是另一个线程。那为什么还要区分成BLOCKED和WAITING两个状态呢,主要有以下原因:

    1. JVM管理的需要,线程放两个队列里管理,如果别的线程运行出了synchronized这段代码,我只需要去blocked队列,放个出来。而某人调用了notify(),我只需要去waiting队列里取个出来。
    2. BLOCKED状态的线程不会被挂起,而是会消耗一定的CPU资源去循环check是否可以执行,WAITING状态的线程则会被挂起
    3. 不同的语义需要,一个是被动BLOCKED,一个是主动WATING

    线程同步算法

    堵塞算法(Blocking Algorithm)

    上面提到的不管是Synchronized的BLOCKED,还是Lock的WAITING,对应到操作系统的线程状态都是堵塞,其本质上都是基于锁的堵塞算法。JVM实现堵塞有两种方式,一个是通过JVM的自旋(Spin-Waiting),另一个是使用操作系统中的挂起(Suspend)。在基于锁的算法中,如果一个线程在Spin或者Suspend的时候持有锁,那么其他线程都无法执行下去,也就是被阻塞了

    1. 自旋(Spin-Waiting):自旋也叫忙等(busy-wait),采用这种方式的线程不会被挂起,而是会消耗一定的CPU资源去循环check是否可以执行了。
    2. 挂起(Suspend):挂起是把该线程从CPU的使用中换出,当可用的时候再让其运行。这种换进换出叫着上下文切换,上下文是需要CPU调度开销的

    如果自旋开销小于上下文切换开销,则推荐使用自旋,反之使用挂起。也就是说如果竞争不激励,自旋高效;竞争激励,线程等待时间长,挂起更高效。

    总结

    虽然Synchronized和Lock在同步机制和性能上无差,但是在使用上还是有些差别的,具体比较内容如下:

    Synchronized

    • 优点:实现简单,语义清晰,便于JVM堆栈跟踪;加锁解锁过程由JVM自动控制,提供了多种优化方案。
    • 缺点:不能进行高级功能(定时,轮询和可中断等)。

    Lock

    • 优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁
    • 缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪

    在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的,可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用Synchronized

    展开全文
  • synchronized和lock

    2014-02-17 14:03:21
    Java为此也提供了2种锁机制,synchronized和lock。做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方。   我们先从最简单的入手,逐步分析这2种的区别。   一、...

    在分布式开发中,锁是线程控制的重要途径。Java为此也提供了2种锁机制,synchronized和lock。做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方。
     
    我们先从最简单的入手,逐步分析这2种的区别。
     
    一、synchronized和lock的用法区别
     
    synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
     
    lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
     
    用法区别比较简单,这里不赘述了,如果不懂的可以看看Java基本语法。
     
    二、synchronized和lock性能区别
     
    synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。
     
    说到这里,还是想提一下这2中机制的具体区别。据我所知,synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
     
    而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
     
    现代的CPU提供了指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
     
    我也只是了解到这一步,具体到CPU的算法如果感兴趣的读者还可以在查阅下,如果有更好的解释也可以给我留言,我也学习下。
     
    三、synchronized和lock用途区别
     
    synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
     
    1.某个线程在等待一个锁的控制权的这段时间需要中断
    2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
    3.具有公平锁功能,每个到来的线程都将排队等候
     
    下面细细道来……
     
    先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)
     
    这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:

    1 public class Buffer {   
    2  
    3     private Object lock;   
    4  
    5     public Buffer() {
    6         lock = this;
    7     }   
    8  
    9     public void write() {
    10         synchronized (lock) {
    11             long startTime = System.currentTimeMillis();
    12             System.out.println("开始往这个buff写入数据…");
    13             for (;;)// 模拟要处理很长时间
    14             {
    15                 if (System.currentTimeMillis()
    16                         - startTime > Integer.MAX_VALUE)
    17                     break;
    18             }
    19             System.out.println("终于写完了");
    20         }
    21     }   
    22  
    23     public void read() {
    24         synchronized (lock) {
    25             System.out.println("从这个buff读数据");
    26         }
    27     }
    28 }
    展开全文
  • Synchronized和lock

    2019-10-30 18:57:38
    synchronized是Java的关键字,当它用来修饰一个方法或者一...Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死...
  • synchronized 和lock

    2018-01-11 21:02:00
    参考:https://www.cnblogs.com/dolphin0520/p/3923167.html ... 1.sysnchronized A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的...
  • synchronized lock

    2018-04-10 14:27:50
    http://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1
  • synchronized Lock

    2016-10-25 11:30:56
    在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock。 1.synchronized   先说一下互斥锁的概念,互斥锁就是可以达到互斥访问的锁,在多个线程要访问临界资源的请款下,如果一个线程在访问的...
  • synchronized和lock的区别

    2021-02-20 13:54:57
    目录标题synchronized和lock的区别synchronizedlock synchronized和lock的区别 synchronized lock
  • 并发编程中,锁是经常需要用到的,今天我们一起来看下Java中的锁机制:synchronized和lock。 Synchronized 和 Lock的概念 Synchronized 是Java 并发编程中很重要的关键字,另外一个很重要的是 volatile。Syncronized...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,240
精华内容 2,896
关键字:

synchronized和lock