synchronized同步代碼塊鎖釋放

今天發現本身寫的線上程序出現數據庫不能同步的問題,查看日誌已經中止記錄,隨後使用jstack查看線程的運行情況,發現有個同步線程鎖住了。java

如下是jstack -l 637  問題線程的內容。mysql

"schedulerJob-t-291" #314 daemon prio=5 os_prio=0 tid=0x00007f7d64844800 nid=0x3d5 runnable [0x00007f7d3a107000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at com.mysql.cj.core.io.ReadAheadInputStream.fill(ReadAheadInputStream.java:101)
	at com.mysql.cj.core.io.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144)
	at com.mysql.cj.core.io.ReadAheadInputStream.read(ReadAheadInputStream.java:174)
	- locked <0x00000000f13c2050> (a com.mysql.cj.core.io.ReadAheadInputStream)
	at java.io.FilterInputStream.read(FilterInputStream.java:133)
	at com.mysql.cj.core.io.FullReadInputStream.readFully(FullReadInputStream.java:58)
	at com.mysql.cj.mysqla.io.SimplePacketReader.readHeader(SimplePacketReader.java:60)

     ..........
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy91.saveAll(Unknown Source) at com.chenerzhu.crawler.proxy.pool.service.impl.ProxyIpServiceImpl.saveAll(ProxyIpServiceImpl.java:51) at com.chenerzhu.crawler.proxy.pool.job.scheduler.SyncDbSchedulerJob$1.call(SyncDbSchedulerJob.java:95) - locked <0x00000000f0745c78> (a java.lang.Class for com.chenerzhu.crawler.proxy.pool.job.scheduler.SyncDbSchedulerJob) at com.chenerzhu.crawler.proxy.pool.job.scheduler.SyncDbSchedulerJob$1.call(SyncDbSchedulerJob.java:55) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - <0x00000000f1cb9350> (a java.util.concurrent.ThreadPoolExecutor$Worker)

 

查看代碼發現代碼中有這麼一段spring

FutureTask task = new FutureTask(new Callable<ProxyIp>() {
                        @Override
      public ProxyIp call() {
         ......
          synchronized (SyncDbSchedulerJob.class){
              proxyIpService.saveAll(availableIpList);
              availableIpList.clear();
         }
        ........
      }
}

........

try {
            ProxyIp proxyIp = proxyIpFuture.get(10, TimeUnit.MINUTES);
            if(proxyIp!=null){
                    proxyIpList.add(proxyIp);
             }            
     } catch (InterruptedException e) {
       log.error("Interrupted ", e); 
    } catch (Exception e) {
       log.error("error:", e);
}        

 

FutureTask中的synchronized批量保存數據,而Future獲取使用了超時限制10分鐘,因爲數據量過大,同步時間超出10分鐘了,中止了執行,而synchronized還未釋放鎖。致使線程鎖住了。sql

最後經過減小每一次批量執行的數據到1000條,成功使synchronized代碼塊執行完釋放鎖。數據庫

 

===================================================================bash

總結下使用synchronized同步鎖釋放的時機。咱們知道程序執行進入同步代碼塊中monitorenter表明嘗試獲取鎖,退出代碼塊monitorexit表明釋放鎖。而在程序中,是沒法顯式釋放對同步監視器的鎖的,而會在以下4種狀況下釋放鎖。socket

一、當前線程的同步方法、代碼塊執行結束的時候釋放ide

二、當前線程在同步方法、同步代碼塊中遇到break 、 return 終於該代碼塊或者方法的時候釋放。spa

三、出現未處理的error或者exception致使異常結束的時候釋放.net

四、程序執行了 同步對象 wait 方法 ,當前線程暫停,釋放鎖

 

在如下兩種狀況不會釋放鎖。

一、代碼塊中使用了 Thread.sleep()  Thread.yield() 這些方法暫停線程的執行,不會釋放。

二、線程執行同步代碼塊時,其餘線程調用 suspend 方法將該線程掛起,該線程不會釋放鎖 ,因此咱們應該避免使用 suspend 和 resume 來控制線程 。

相關文章
相關標籤/搜索