synchronized同步代碼塊鎖釋放


今天發現自己寫的線上程序出現數據庫不能同步的問題,查看日志已經停止記錄,隨后使用jstack查看線程的運行狀況,發現有個同步線程鎖住了。

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

"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)

 

查看代碼發現代碼中有這么一段

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還未釋放鎖。導致線程鎖住了。

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

 

===================================================================

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

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

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

3、出現未處理的error或者exception導致異常結束的時候釋放

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

 

在以下兩種情況不會釋放鎖。

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

2、線程執行同步代碼塊時,其他線程調用 suspend 方法將該線程掛起,該線程不會釋放鎖 ,所以我們應該避免使用 suspend 和 resume 來控制線程 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM