<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Nesive</title>
  
  
  <link href="https://www.nesive.com/atom.xml" rel="self"/>
  
  <link href="https://www.nesive.com/"/>
  <updated>2026-11-04T14:29:58.421Z</updated>
  <id>https://www.nesive.com/</id>
  
  <author>
    <name>Nesive</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>百年传承-接力棒-为什么我们在一起会更好？</title>
    <link href="https://www.nesive.com/3af6ba76.html"/>
    <id>https://www.nesive.com/3af6ba76.html</id>
    <published>2022-07-20T14:00:15.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p style="text-align: center">友谊的本质，<br>其实是一种关系。</p><p style="text-align: right">亚里士多德</p></blockquote><blockquote><p style="text-align: center">两个人总比一个人好，<br>因为二人劳碌同得美好的果效。</p><p style="text-align: right">所罗门 - 传道书 4:9</p></blockquote><hr>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p style=&quot;text-align: center&quot;&gt;友谊的本质，&lt;br&gt;
其实是一种关系。&lt;/p&gt;
&lt;p style=&quot;text-align: right&quot;&gt;亚里士多德&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p styl</summary>
      
    
    
    
    <category term="信仰生活" scheme="https://www.nesive.com/categories/%E4%BF%A1%E4%BB%B0%E7%94%9F%E6%B4%BB/"/>
    
    
    <category term="百年传承" scheme="https://www.nesive.com/tags/%E7%99%BE%E5%B9%B4%E4%BC%A0%E6%89%BF/"/>
    
  </entry>
  
  <entry>
    <title>百年传承-接力棒-百年之后会怎样？</title>
    <link href="https://www.nesive.com/e8b1137f.html"/>
    <id>https://www.nesive.com/e8b1137f.html</id>
    <published>2022-07-19T21:40:49.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="抱歉，这个密码不正确，请您再试试" data-whm="抱歉，本篇文章不能被校验，不过您还是能看看解密后的内容">  <script id="hbeData" type="hbeData" data-hmacdigest="76030c0e6ed85542ea33d753837e945ebab2445aa5e0be26d8b4086701453b7a">ed28f66e8a6a336c8c5ac38ade1d902a6532a97a25ca6edf26ba4effb8e1e8aa05fadafb80c4b64702550dba114a9b2e4c3f5bf78d966d07b45c39075babc4f020b2f8d0890aae90c93379488faf481e0095cca26055c44ba4678e3edfa74de69aaf08d4dddf1d674f224aca595d0aee476ba1807c89843504fa004e28f650952e7597d65a60b764fce17d44f39028549e446bceff72b6fee940c4f878d6ed56795012ec7b69a911bbaef914c81b0d11a330e744a7700179d63e8e2c3d5ee536c8f5713fd7f7118e0e1a6b0d000cfb19ea854f1f1ff1bae967267b05aaffe8c5861672d480cbfcd230e8ab59e2c7e94d7bfe1d90d4a45cedae586be7dfe33e7b18a7e1ba4f73ac37083882bbbf7282c93d0b5c79a4e5c5490e8314f006bc2eda6fd5460f5db3595f7e3bd88ab0dd8a58470ccf0107323daa64b8295e6113ebf5b0668a3eae6551455f95d31166125675aed5d7fb7570f643a273c264a62af6d4422cf067086877ef502b77ffbcd6d4fbdf62cca089c2a193063045f16792f41f9e2a73ba2afacea95bd6400f86ffefe67f1536fed4e20ee8278759c976ffe24f43e14799ba1dee013e61e9a76da690364afe6afce0cd78b213f31ba8acb9d48d2426b08f6fa17979493f289d308d0d53aa32ff097232e668e123d15c157232c26361bdbe34ba38602a1d71a475cd0ec81392cbdb4da5706ec30954e2e95659b2cc4c0bf7ec871ce633f1b14f0ef30bf31b0a7fb978ec82ad1cbd31c027a3f2395d53f5aaeeeb049e4c849928f2e798dada0304adb3904f47573397919bc00504158eb09414c148cd4059f261216e45873e0c8f37aad962558a9d8830cccedc0c7464341e72903c9195df4731bc42ce5617a588db9815e01b1b60c3c73378b244eecc8f8f969ad6df7aa575c32d90f5b04dd7bbc935f911939e29c69a63bdcb54480d34e44025ce332ff92ab49f9ea2ea3880e2bd53383dea93f769c0072e07aacd7e426e5aaef04621468e2e96533f7d5ef76e35d9ee64d9bc2848eaaf19da201bac75b1ddab3031f3800b047264faf0a0027ca0faeeb39da3632101d5ded9965dc9cda44582bed06f436d6c6c3c8914c7cb9bf3a91205f126c56536cbbfc420b85bb772a7d0d75e6de514d6a06d921dd892765e5b3a1877d4ab0c8180dc85e603992345a4320527e91308c74e98b9ea64ece3ed4116256c8814fe60643b352193930ab491488049b461104b8c5ef7c28a6389c3ea458fc4e1d12845a6913e8230997148ed911020bb6b750d61ffc766b40eb55306d97aca2a4d65df427c8c691e412393e382f2a806622f5458bbaa733a365db6e9739f72df6e23069741d34b544dcf0bf4d9a04d86e3ffac3db77f9f30fea8fa303d6ada4d18259b8bafef959d9bc65261246e613c0dae02d8d2711e92b9c0459b352302021aaaa3d14121cfa50197a3f8b4912461781c56a19c995fc5790fa73accf70b49e35e0927bff116c5dfb23a16b5968ed49efbee996e02544161719e156a75eda2f5b0cdd2abc835a7b93cc61693c1b96ed55b005b078a3a475bf52c982727dcf1b75b2d5bf7a54821d68b7c5cb73c9d10cbc0531327cfbf9fa795d972e32b76ad121a4e49b80f24aa3610d27f2de446403d51bbb2380b43788ff82e7b6dd92bdbf482e57e9adba499b204b2f9aafa46812343326fa5e6256f1ef4a0334743560fbc5459411756fb5a6a15de0b2a567f75195718d56d2e83f43f740b790aab573463445c689870eef000680c2d7c03bad5d00715efe3fbd7e676ed2a92da9eacda2a1b95a8c381f8c2c44ca2ac551d314dd3871a2706f5f1ed1bbfcc6d2161211c986d0b5c9758116ff663379f87c69a8a5b82869123b3f828c79bd75fe579fe537ba7ef4208b1274bb4dc36aeffed6641c2fa1326344139f0ce149fa58b865607d90a20c7b3b0ece2fbd2d21a795d95a9d16e0da504d533f9b68eda4ac45c6a77771b30a6e79a0341dac1d6a340ed94a22d96acc49a6a5370a8e7d045f1d92fed1da1d2d20f11d951c8f4a9e7c1f1bed7d7f34c69eb1dc6cb2a6076154f218579aebb44cbdcfa39daa1347b9412a565164d9203b847faa39d6976cfcf241523649b4c109ed635e5cc804c661505782aace6a57a0f257d0bc661756ecee1a637bf4f48ba5d62454cfb3efe6f5c048f2b8932a7959dc46db2e68545cde9cb2e45d51b1c04668e411797fd5f06b4b5607240b4d216171fb993703f5b84c4213caff009bcdbdb632a773cacd588977e7dfa45bdf1fa64acc9c9b72543628ccc00f9031fdac7b8ccd543e8c2e740c046ba70e99c6246209a594c8e4a1e55ee331a8781e66fbd8398847bb1e32fb82349be20be93d7c0d956edd1ab909e13da2aba00f2660a86fb63005dc7ff04cc948432c3207b796d39daec1d6b8e08d4c9d53df1c401de827c452d217614ef035237c6d3b1f77fed5237ff2a045a5b007ac34f4d61021b8d4d47d9375939e62b073334099406046f1a49be8d164e8723c3a08f05845970fc35080bbd66be3661d643b26b8f9910c35a8c37621721192351cc9d4571261b22bed7c29d9aea37017a7ba70508edd27f7fa71e09d784f0c12491b2616244b21525af2125cd3ed15c91b4fa940d332fda2165ba9a523ebb0d34940e2c2c2d5906b25639cdfa9c1a9d15b0b035b565485601ac7c30f02712fa65f3eb1b7584f540feb83b07c0b0c6f5164438086bca89c674f4824b375c13ba252c731ede00316ccd4d53c74a54b600b56c4db3d4597ad7ae679404d2e0a767767370a7a6600bbb238633390b800a5fb339ac35be19b8a754164c9bdf286cfac95accea707e830987636b40b38c970c77b5585420892735849efb5f08c22f243912ea3b8f576114579649f49a85f495d40cc7193e802941b8fa64c24f22769c4a4e62eeaf9e9eb8649bc48842565e679c06c9e2616cd69aa07c179fd04fd6c5c212b3dbef421b3bb063fe02f564d9687f656f8150d4ae5e876f566e695e36a823b5b67e67dd37b11833bc85ff881f3874ea2de639f6322fff35913ec6da3be09cd5f89671abce11c021b99d194fd6c6b4750f4fe69591dbd0fb3f99fcce26431512b71dd76a2b47a13fc8f21f5aeef3ca08ed7aa9167c6c3d5f1c4ba29e405db241f5bce242c5e3b82d1dce4c34476d8fb23d33197c983a84c9d0cf0d8a9c1b3a8ac3205ed367411b73aba5b076d594e5abd174682694062558ed4a5e9123753c8ecbd0db3cc3ce798dda84cc11e838c03551b17f1b6acfc3d1c599d32143f865655a32f12c0e3993144c70ba3be797c84682ee45c932a4f07c9d82b265ec2ae449a8aa2ef997c263b728387f4e6d5c6cc26023a5b1b4ce593497859da3ebc96342d10962ebe2f182d026b42af18e84e3266dc24b668de64c637d4458ae8e1dc3ba11f95b3cd05d465a5392429304f9464aed08d6bd56c3d72752cc54c1e141d6fac5e617ed0e6a401b3ac765e269773b3ae3cfcb345859086655935e5a51558556729f85008c15c72c5549a529fd3f78cabc7ba47352f83e8284306e9e74ae9edec69bdd751b6d39ae401264c793f0fe587039b3a16303d433c83d4801e797e74fd9cf95991b268e551504ca67b137e629444aa45c4cb7160cb5de2ca1666d7d183d2f84ca7cf0b8c146adfb04c71943844a8e339d15aaee8afde2f66170cb52d82b075613ee0370626c98c40d3f45332d75d1414a4e91bebf7dbf9961f105cd74a47942e69d4a331882a83bf6ca56549e6f2c254cc5499321fbeb76f7c5c19ec480e270242b07b76f810e30cae763b0e1af8fd9b533dd51cc036c56aa670e238dd71cd0c833404455881c08e790e06e8e45731e54b8a78895bc833059a933ffc657e5fe761ccf05550e983e66946ce32103f7767ce35bb8328b0a2ecc4bed27e4ffa4622532be545c3edb441fdbd95794f4c10aefadb885a7eb94b6a8d42ecdacfbd43c66cc8c2c12fba4bdd318b36c062075fe59e0d5844c1b2334bdf72f4ed36d7e2621db914b9c825241994dedfaafbf12e3fe9a0420b5f4af89d228b9061e49e1a4ef07d9d11a89f0ebfa271047cc0d4faa7cc112fba6689b2cc99cf1127c38c6601e35d8ebada48747414b036dc93dadc14434fa0be2f2104304dab9f0374dd3cbdd011aeae37ce64c498b8f9a9b7c4eb7244cf92c27e19a205c3689edd72445121dd0046f491a5730defd5cce19ef7d836fff5043fcb306566567e8787899f90230b652a9e3b1f4a498becc6cbe81476d0548cb8031462bad5f4bcd22525853dfc73bb95ab2306de14b7eb8ef26a6837f1029a567575a44729c7d8342b81cc1cc58b713f31e3286524020173fc2b8bd27546431bc4cd7a78796635c4da486cc3d6d8af6ee6d57584b81006378a44ba98dcac61296026a6a1c5c22014c5d8db6e1679ec7d98484d52f01e14779ac5b140d1590538014088677cb16e9541679a2066b7615c09c75b5c41fbb38ae91b897894ad2664fe28949498e34ed9fde39e1e6c8e07440be372a7b6cb99c12af671df8e6ae0af944d91bb4f0a2e9ce8297b889b6822ac936acb0498200a717a9c25a0f4c479bcb423e7f9d6f5acf3a34828eed7b1ac4335a12050be95b2f8b1195cdb53b51486cbe4368ba28a67a40c430ada8e69214b0813eb7aec7c2c3044ce8a6bcbe711817beba74bd6c6cf2d6bf6ab4e8ede67dc973cd06c528cbc763df5f649dcd4b9c4e528c6fd45e8317255316f77f37480f1ee080acf0417e783a02e8bc1724b45c6aa6dc9f229ba088db09ed4b444a0ee71c3c901877a68bf9adbe5b38c2d2a999ce1aa7a7918e8ccd336e1c90070695918fd58239c6d216dafb4d30a7301b4a7231fb8fd02ad12192a3f750a6b4c12aa54ded575bd66d5858ca729ebc74423a75aa3ba14e6eef2e706a0f4ee9fcbb9e82174977a5a66f5ca0e49ac08896e017df3747539842313e4a14a90f9a5a07f6f8bbb3754cd3e439025ea1b6a15a7a3b564d218a716c9dd7b68ee46d4d081e412862a086a8d5cd6ab5de9df070228424359d3ae88de40644ccd301b81543af6eb5ed08f9c20b750cd54d4104336339d6fde21e93ba6c8db6ad5e817fc28a18430e8a811a3b068f618fd22e8ee8f4a239af9b7333c658863cac4518cb68adec16953af2aaf6e8efd99f2f8c2ec5ef8bdf9a7a1a7633965062819a558e6c04f82de87e5e6ce0d12457b9126f39d97ad21315ed049e0b36dcba1fbbc52a5452c2d4c26eb9b1f283503e0c49ee48d69a22163d1e5816133ca52f108ddb370c2f58cef7787d1319928e6eff15594fb72e854c017d3d0484144e9b54f4ab9b3d693a736622ac56a7d0dc281b3f31f9b35bd4a8816aedb315d4a8176cc34446f5b0cafcbdcabb58ac7c700cb645e2f0c7be664dcde3860e2470a0a34b11d26cd731964f30481399441e2ea84d13ae36d1b04c181d07fc260f12c85b3a0a6762f017ecff90737502c8575f4f3bd9e7c54bdb8ec10a0d0ded53f6b78662ea63754e1fe02ca6bc6193a67354524382bedeb191ebe38b6a6ae95b18d56558d2093dda5d0a1444a0dbeb561a7efc4e543e84045f736059fd7bf619fc4b76355f6a0b9cb9869636fc67640bec39ebaec13b4d4192d00fcba3a62fa983a3d4a3c97ff1c3fdf549649d0223c4c8c4e0d7c1faed00a4824e6048f472c596d25bea62241a1e2f46bc77425652b972fd2c01229385022e722ee28ca45e3bcbce7281573fb5e574a6802a1fe4bb6a9c7eee6b9c3e79ad4ee8460dcd9d14e6b964a53100e7b1f005bb5bf488428e2c8b28a84b277e31fda524c8ff5bd0bd629192d5a36160de37ef7e2cee637cd64faf0d4ed847d606a9f3c8952c8728230e379b66b9aef0f21f2341a2e139ca57fb8efe70bd35e7242bcdfcefca1f783ba5e63368b9123e04c2bac5302117f3ca2247fa16bd40f58e365f1f48e6e52e1d32399bfdb90fb50895611a95ea8f198e0b31a073c16d5ca5a522143995141b1646360cd35dc881cafbb945dde03f8a4a75b8a498318e7045b206fd47cbd21772d959e8c3267197c2504f39809b9728c0c1db1eb2668b615476ceafedb0470328c39a405c5ec66c2adeb6f3ebe44338439f59555ca26809e35ef3c62ddcd7680cf01feebdb99ffa60dea1763bed9ee3a635c6b04afa8fbcbac64e2e99c50a942f0d711f3aea4b16bf1d48b0e32c7122edef4415a9f3438a9ecf7284660ef8506837a299f2f0ac9c06ee63890749becaf2ce4ff4b12be6b968a6ac93f73b72446a3072dd3863ecf13124fcead63ac849d8c2d1c2f8f43d0de8e8cc6dfa8e3e20ba85181c367dd38c9e2fc2d671a1e7ef6f9e1cd2db7f10a2caf353f87300f34ee3edec564ad11622bfd57f5aafe17666cae7ca3ebf8824896c2cb26a350b8a78b631a570f2f7cf75678a9789ca392da463877a74e9251599744d29c273b236c45964016bbab41160a7ba8ba0b4d437ed7f7e19424d0caea4d9d0ac18f5e9a87fcd43c1d1c44b3e847167c6a98d76301efa3b3c5380a6ff7f53000727a6f83872b073a8dcb29da7bfca6a393206c3dd5e15f8f7ff485b5ecd9cd96ac47a9bc8ee9231a10b3f6901351f1c31967d20ba520fad31bd3de29e9257a8541d499ebdb9ee8c69203536706e69c452877a71a48ae7f237b716fe2baef3860db25ba953a5605cc931665947d6bcc6b53b99d36478527e7f3da98c33c9610bc4abd0150d36949df3390785ecdbc1c50f6e7dfe10113842cfe1935b837fff3da5b4d16e93f6445464f46213efbeb1a86462bb2a5a6fefbddf7ed31a4c0e3a5c6278feccb6b72b27fb3f2e4e56093eb51b38fa5e561aa1f5c257a18891f03e9f3461e1f9616e31ae7475f71d766975f6f507c814056dd3ac8c62701b34e65f4e952db317be8b9eaa16797ed51b81cda77200002bd604edd47fc739284e4150532748497ab414b0adc52d95ddbbde1b94b26b1084e295ba8d63155a8ffb4c505698b0a89aad2a15561c0ab4317eb61832101926b79fcae85b6b71a112968a4c2cab82caa3f65efb007aa4777b86745324856f7392f35c4be924547761f22b1bd3bb876a5582db1cc97e4dd24da829118d204d1585ea9df5a72b1ed761fb888e11416501167846e8f34300b1abf06925441a3c4e73ad3e104386ad2afb881c5336403ee531160e4b38c704b3ff32e0de6683addc09c6ef1271ff03e99fe40025748c9139f5e8f63887d38d451de1cf5addfdc2f11ca957d156d1fc00173bfb90598e443febb118e2655a302919b10fc9912c040db91c2f06ada9171909e9fcf4d05a3b67d9e08f15dececb0a30b82d61e552521d39bb826b55f07af8a0ce613e0bdc1ec7d2562e00cc240a073bf07f53eaeda3712accf1b2448414bc9a150d383414c1acc99ab8902fd9c2e7f3999bfb9b9d38af3af4e345dd09c40dfb0930eccfa22177bb912a1df07b15a72c02c7c8bbfbbf2c08f644b3b645c76efe2a8079442989c792b240fe7678e61c124fadf909458f7dfcb29b5da23f4e830214ca27830de7f74e23c392e4b11f1987a41924ed4bb2d5ac1c86409f47b9faa79f0b35537b2fbb7be4e76f5a152bd01c0abc77cddc362422d6160fad8a9e4a734e3c0ae76f98bd44d5866bf164b0186f9f4c7463a5b67fb35c22fa16e677042601c8581fade18bc148dbc52cd8240d84776fd1276d3d01b57b172e064c855a14e8fb373f3a88d7702710b8430f96398565580cdff71629489ae529d6</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-wave">      <input class="hbe hbe-input-field hbe-input-field-wave" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-wave" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-wave">您好，本篇为加密文章，请在这里输入密码</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-wave" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">请输入密码查看</summary>
    
    
    
    <category term="信仰生活" scheme="https://www.nesive.com/categories/%E4%BF%A1%E4%BB%B0%E7%94%9F%E6%B4%BB/"/>
    
    
    <category term="百年传承" scheme="https://www.nesive.com/tags/%E7%99%BE%E5%B9%B4%E4%BC%A0%E6%89%BF/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-JMM与happen-before(6)</title>
    <link href="https://www.nesive.com/b25d002f.html"/>
    <id>https://www.nesive.com/b25d002f.html</id>
    <published>2022-04-09T17:23:03.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-为什么会存在“内存可见性”问题？"><a href="#1-为什么会存在“内存可见性”问题？" class="headerlink" title="1.为什么会存在“内存可见性”问题？"></a>1.为什么会存在“内存可见性”问题？</h2><p>要解释这个问题，就涉及现代CPU的架构。如下是x86架构下CPU缓存的布局，即在一个CPU4核心下，L1、L2、L3三级缓存与主内存的布局。每个核上面有L1、L2缓存，L3为所有核共用。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/x86_cpu_cache_layout.png" alt="Java并发原理-x86架构下CPU缓存布局"></p><p>因为存在CPU缓存一致性协议，例如MESI,多个CPU之间的缓存不会出现不同步的问题，不会有“内存可见性”问题。</p><p>但是，缓存一致性协议对性能有很大损耗，为了解决这个问题，CPU的设计者们在这个基础上有进行了各种优化。例如，在计算单算和L1之间家林Store Buffer、Load Buffer(还有其他各种Buffer)，如下图所示。</p><p>L1、L2、L3和主内存之间是同步的，有缓存一致性协议的保证，但是Store Buffer、Load Buffer和L1之间确实异步的。也就是说，往内存中写入一个变量，这个变量会保存在Store Buffer里面，稍后才异步地写入L1中，同时同步写入主内存中。</p><p>注意这里只是简要花了x86的CPU缓存体系，还没有进一步讨论SMP架构和NUMA的区别，还有其他CPU架构体系，例如PowerPC、MIPS、ARM等，不同CPU的缓存体系会有各种差异。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/x86_with_buffer.png" alt="Java并发原理-x86加了Store Buffer和Load Buffer的CPU缓存体系"></p><p>但站在操作系统内核的角度，可以统一看待这件事，也就是如下所示的操作系统内核视角下的CPU缓存模型。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/os_cpu_cache_model.png" alt="Java并发原理-操作系统内核视角下的CPU缓存模型"></p><p>多CPU、每隔CPU多核心，每隔核心上面可能还有多个硬件线程，对于操作系统来将，就相当于一个个的逻辑CPU。每隔逻辑CPU都有自己的缓存，这些缓存和主内存之间不是完全同步的。</p><p>对应到Java里，就是JVM抽象内存模型，如下图所示。</p><p>到此为止，介绍了不同CPU架构下复杂的缓存体系，也就回答了为什么会出现“内存可见性”问题。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/jvm_memory_model.png" alt="Java并发原理-操作系统内核视角下的CPU缓存模型"></p><h2 id="2-重排序和内存可见性的关系"><a href="#2-重排序和内存可见性的关系" class="headerlink" title="2.重排序和内存可见性的关系"></a>2.重排序和内存可见性的关系</h2><p>Store Buffer的延迟写入是重排序的一种，成为内存重排序(Memory Ordering)。除此以外，还有编译器和CPU的指令重排序。下面对重排序做了一个分类：</p><p>1.编译器重排序。对于没有先后依赖关系的语句，编译器可以重新调整语句的执行顺序。</p><p>2.CPU指令重排序。在指令级别，让没有依赖关系的多条指令并行。</p><p>3.CPU内存重排序。CPU有自己的缓存，指令执行顺序和写入主内存的顺序不完全一致。</p><p>在三种重排序中，第三类就是造成“内存可见性”问题的主要原因，下面再举例来进一步说明这个问题，如下所示：</p><pre class="line-numbers language-none"><code class="language-none">线程1：X &#x3D; 1a &#x3D; Y线程2：Y &#x3D; 1b &#x3D; X<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>假设X、Y是两个全局变量，初始的时候，X=0，Y=0。请问，这两个线程执行完毕之后，a和b的正确结果应该是什么？</p><p>很显然，线程1和线程2的执行先后顺序是不确定的，可能顺序执行，也可能交叉执行，最终正确的结果可能是：</p><pre class="line-numbers language-none"><code class="language-none">(1) a &#x3D; 0,b &#x3D; 1(2) a &#x3D; 1,b &#x3D; 0(3) a &#x3D; 1,b &#x3D; 1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>也就是不管谁先谁后，执行结果应该是这三种场景中的一种。但实际可能是 a = 0, b = 0。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-为什么会存在“内存可见性”问题？&quot;&gt;&lt;a href=&quot;#1-为什么会存在“内存可见性”问题？&quot; class=&quot;headerlink&quot; title=&quot;1.为什么会存在“内存可见性”问题？&quot;&gt;&lt;/a&gt;1.为什么会存在“内存可见性”问题？&lt;/h2&gt;&lt;p&gt;要解释这个问</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-volatile关键字(5)</title>
    <link href="https://www.nesive.com/56ec45e8.html"/>
    <id>https://www.nesive.com/56ec45e8.html</id>
    <published>2022-04-09T16:43:59.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>volatile这个关键字很不起眼，其使用场景和语义不像synchronized、wait()和notify()那么明显。正因为其隐晦，volatile关键字可能是多线程编程中被误解最多的一个。而关键字越隐晦，背后隐含的往往越复杂、越深刻。接下来将进一步由浅入深地从使用场景讨论到其底层实现。</p></blockquote><h2 id="1-64位写入的原子性（Half-Write）"><a href="#1-64位写入的原子性（Half-Write）" class="headerlink" title="1. 64位写入的原子性（Half Write）"></a>1. 64位写入的原子性（Half Write）</h2><p>举一个简答的例子，对于一个long类型变量的赋值和取值操作而言，在多线程场景下，线程A调用set(100),线程调用B调用get(),在某些场景下，返回值可能不是100.</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Example1</span><span class="token punctuation">&#123;</span>    <span class="token keyword">private</span> <span class="token keyword">long</span> a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token comment">// 线程A调用set(100)</span>  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">set</span><span class="token punctuation">(</span><span class="token keyword">long</span> a<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">this</span><span class="token punctuation">.</span>a <span class="token operator">=</span> a<span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span>    <span class="token comment">// 线程B调用get(),返回值是不是一定为100?</span>  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>a<span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这有点反直觉，如此简单的一个赋值和取值操作，在多线程下面为什么会不对呢？这是因为JVM的规范并没有要求64位的long或者double的写入是原子的。在32位的机器上，一个64位变量的写入可能被拆分成两个32位的写操作来执行。这样一来，读取的线程就可能读到“一般的值”。解决办法也很简单，在long前面加上volatile关键字。</p><h2 id="2-内存的可见性"><a href="#2-内存的可见性" class="headerlink" title="2.内存的可见性"></a>2.内存的可见性</h2><p>不仅64位，32位或者位数更小的赋值和取值操作，其实也有问题。在之前讲过，线程关闭的标志位stopped为例，它是一个boolean类型的数组，也可能出现主线程把它设置成true,而工作线程读到的却还是false的情形，这就更反直觉了。</p><p>注意，这里并不是说永远读到的都是false,而是说一个线程写完之后，另外一个线程立即去读，读到的是false,但之后能读到true,也就是“最终一致性”，不是“强一致性”。这种特性，对于之前的例子没有太大影响，但是如果想实现无锁算法，例如实现一把自旋锁，就会出现一个线程把状态设置为true,另外一个线程读到的却还是false,然后两个线程都会拿到这把锁的问题。</p><p>所以，我们所说的“内存可见性”，指的是“写完之后立即对其他线程可见”，它的反面不是“不可见”，而是“稍后才能可见”。解决这个问题很容易，给变量加上volatile关键字即可。</p><p>“内存可见性”问题的出现，跟现代CPU的架构密切相关，后面会详细探讨。</p><h2 id="3-重排序-DCL问题"><a href="#3-重排序-DCL问题" class="headerlink" title="3. 重排序:DCL问题"></a>3. 重排序:DCL问题</h2><p>单例模式的线程安全写法不止一种，常用写法为DCL(Double Checking Locking)，如下所示：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Singleton</span><span class="token punctuation">&#123;</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance<span class="token punctuation">;</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">SIngleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      <span class="token comment">// 为了性能，延迟使用synchronized</span>      <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token class-name">Singleton</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">if</span><span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>          <span class="token comment">// 有问题的代码！！！</span>          instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Instance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>       <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>     <span class="token keyword">return</span> instance<span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上述的**instance = new Instance()**代码会有问题：其底层会分为三个操作：</p><p>1.分配一块内存</p><p>2.在内存上初始化成员变量</p><p>3.把instance引用指向内存</p><p>在这三个操作中，操作2和操作3可能重排序，即先把instance指向内存，再初始化成员变量，因为二者并没有先后的依赖关系。此时，另外一个线程可能拿到一个未完成初始化的对象。这时，直接访问里面的成员变量，就可能出错。这就是典型的”构造函数溢出“问题。解决办法很简单，就是为instance变量加上volatile修饰。</p><p>通过上面的例子，可以总结出volatile的三重功效：64位写入的原子性、内存可见性和进制重排序。接下来，我们进入volatile原理的探究。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;volatile这个关键字很不起眼，其使用场景和语义不像synchronized、wait()和notify()那么明显。正因为其隐晦，volatile关键字可能是多线程编程中被误解最多的一个。而关键字越隐晦，背后隐含的往往越复杂、越深刻。接下来将</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-wait()与notify()(4)</title>
    <link href="https://www.nesive.com/143e883d.html"/>
    <id>https://www.nesive.com/143e883d.html</id>
    <published>2022-04-09T15:15:48.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-生产者-消费者模型"><a href="#1-生产者-消费者模型" class="headerlink" title="1.生产者-消费者模型"></a>1.生产者-消费者模型</h2><p>生产者-消费者模型是一个常见的多线程编程模型，如图所示。</p><p>一个内存队列，多个生产者线程往内存队列中放数据；多个消费者线程从内存中取数据。要实现一个这样的编程模型，需要做以下几件事。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_producer_consumer.png" alt="Java并发原理-线程的生产者-消费者模型"></p><p>1.内存队列本身要加锁，才能实现线程安全。</p><p>2.阻塞。当内存队列满了，生产者放不进去时，会被阻塞；当内存队列是空的时候，消费者无事可做，会被阻塞。</p><p>3.双向通知。消费者被阻塞之后，生产者放入新数据，要notify()消费者；反之，生产者被阻塞之后，消费者消费了数据，要notity()生产者。</p><p>第1件事必须做，第2和第3件事不一定要做。eg，可以采取一个简单的办法，生产者放不进去时，睡眠几百毫秒再重试，消费者取不到数据时，睡眠几百毫秒再重试。但这个办法效率低下，也不实时。所以，我们只讨论如何阻塞、如何通知的问题。</p><h3 id="1-1-如何阻塞？"><a href="#1-1-如何阻塞？" class="headerlink" title="1-1.如何阻塞？"></a>1-1.如何阻塞？</h3><p>方法1：线程自己阻塞自己，也就是生产者、消费者各自调用wait()和notify()。</p><p>方法2：用一个阻塞队列，当取不到或者放不进去数据的时候，入队/出队函数本身就是阻塞的。这也就是BlockingQueue的实现，后面详细讲述。</p><h3 id="1-2-如何双向通知？"><a href="#1-2-如何双向通知？" class="headerlink" title="1-2.如何双向通知？"></a>1-2.如何双向通知？</h3><p>方法1：wait()与notify()机制。</p><p>方法2：Condition机制。</p><p>此处先讲wait()与notify()机制，后面会专门讲Condition机制与BlockingQueue机制。</p><h2 id="2-为什么必须和synchronized一起使用？"><a href="#2-为什么必须和synchronized一起使用？" class="headerlink" title="2.为什么必须和synchronized一起使用？"></a>2.为什么必须和synchronized一起使用？</h2><p>在Java里面，wait()和notify()是Object的成员函数，是基础中的基础。为什么Java要把wait()和notify()放在如此基础的类里面呢？而不是作为向Thread一类的成员函数，或者其他的成员函数呢？</p><p>回答这个问题之前，先回答为什么wait()和notify()必须和synchronized一起使用？先看以下代码：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">&#123;</span>    <span class="token keyword">private</span> <span class="token class-name">Object</span> obj1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>      obj1<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>      obj1<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>或者下面的代码：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">&#123;</span>  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后，开两个线程。线程A调用f1(),线程B调用f2()。答案已经很明显：两个线程之间要通讯，对于同一个对象来说，一个线程调用该对象的wait(),另一个线程调用该对象的notify(),该对象本身就需要同步！所以，在调用wait()、notify()之前，要先通过synchronized关键字同步给对象，也就是给该对象加锁。</p><p>前面讲了，synchronized关键字可以加在任何对象的成员函数上面，任何对象都可以成为锁。那么，wait()和notify()要同样如此普及，就放在Object里面了。</p><h2 id="3-为什么wait-的时候必须释放锁？"><a href="#3-为什么wait-的时候必须释放锁？" class="headerlink" title="3.为什么wait()的时候必须释放锁？"></a>3.为什么wait()的时候必须释放锁？</h2><p>当线程A进入synchronized(obj1)中之后，也就是对obj1上了锁。此时，调用wait()进入阻塞状态，一直不能退出synchronized代码块；那么，线程B永远无法进入synchronized(obj1)同步块里，永远没有机会调用notify()，岂不是死锁了？</p><p>这就涉及一个关键问题：在wait()的内部，会先释放obj1，然后进入阻塞状态，之后，它被另外一个线程用notify()唤醒，去重新拿锁！其次，wait()调用完成后，执行后面的业务逻辑代码，然后退出synchornized同步块，再次释放锁。<br>wait()内部的伪代码如下：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token comment">// 释放锁</span>  <span class="token comment">// 阻塞，等待其他线程被notify</span>  <span class="token comment">// 重新拿锁</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>只有如此才能避免上面所说的死锁问题。后面将Condition实现的时候，会再详细讨论这个问题。</p><h2 id="4-wait-与notify-问题"><a href="#4-wait-与notify-问题" class="headerlink" title="4.wait()与notify()问题"></a>4.wait()与notify()问题</h2><p>以上述的生产者-消费者模型来看，其伪代码大致如下：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">enqueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token keyword">synchronized</span><span class="token punctuation">(</span>queue<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span>queue<span class="token punctuation">.</span><span class="token function">full</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      queue<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token comment">// 入队列</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>      <span class="token comment">// 通知消费者,队列中有数据了</span>      queue<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">dequeue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token keyword">synchronized</span><span class="token punctuation">(</span>queue<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span>queue<span class="token punctuation">,</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      queue<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token comment">// 出队列</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>      <span class="token comment">// 通知生产者，队列中有空位了，可以继续放数据</span>      queue<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>生产者本来只想通知消费者，但它把其他的生产者也通知了；消费者本来只想通知生产者，但它被其他的消费者通知了。原因就是wait()和notify()所作用的对象和synchronized所作用的对象是同一个，只能有一个对象，无法区分队列空和队列满两个条件。这正是Condition要解决的问题。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-生产者-消费者模型&quot;&gt;&lt;a href=&quot;#1-生产者-消费者模型&quot; class=&quot;headerlink&quot; title=&quot;1.生产者-消费者模型&quot;&gt;&lt;/a&gt;1.生产者-消费者模型&lt;/h2&gt;&lt;p&gt;生产者-消费者模型是一个常见的多线程编程模型，如图所示。&lt;/p&gt;
&lt;</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-synchronized关键字(3)</title>
    <link href="https://www.nesive.com/cd0cf4b7.html"/>
    <id>https://www.nesive.com/cd0cf4b7.html</id>
    <published>2022-04-09T00:25:15.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-锁的对象是什么？"><a href="#1-锁的对象是什么？" class="headerlink" title="1.锁的对象是什么？"></a>1.锁的对象是什么？</h2><p>对于不熟悉多线程原理的人来说，很容易误解synchronized关键字：它通常加在所有的静态成员函数和非静态成员函数之前，表面看好像是“函数之间的互斥”，其实不是。synchronized关键字其实是“给某个对象加了把锁”，这个锁究竟加在了什么对象上面？如下所示，给f1()、f2()加上synchronized关键字。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">&#123;</span>  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">fl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span>  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>等价于如下代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">&#123;</span>  <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token class-name">A</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token class-name">A</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span><span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对于非静态成员函数，锁其实是加载对象a上面的；<br>对于静态成员函数，锁是加载A.class上面的。 当然，class本身也是对象。</p><p>这回答了一个关于synchronized的常见问题:一个静态成员函数和一个非静态成员函数，都加了synchronized关键字，分别被两个线程调用，他们是否互斥？很显然，因为是两把不同的锁，所以不会互斥。</p><h2 id="2-锁的本质是什么？"><a href="#2-锁的本质是什么？" class="headerlink" title="2.锁的本质是什么？"></a>2.锁的本质是什么？</h2><p>无论使用什么语言，只要是多线程的，就一定会涉及锁。既然锁如此常见，那么锁的本质是什么呢？</p><p>如图所示，多个线程访问同一个资源。线程就是一段运行中的代码；资源访问就是一个变量、一个对象或一个文件等；而锁就是要实现线程对资源的访问控制，保证同一个时间只能有一个线程去访问某一个资源。比如，线程就是一个游客，资源就是一个待参观的房子。这个房子同一时间值允许一个游客进去参观，当一个人出来后下一个人才能进去。而锁，就是这个房子门口的守卫。如果同一个时间允许多个游客参观，锁就会变成信号量，这点后面讨论。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_lock_resource.png" alt="Java并发原理-线程的状态迁移过程"></p><p>从程序角度来看，锁就是一个“对象”,这个对象要完成以下几件事情：</p><p>1.这个对象内部得由一个标志位（state变量），记录自己有没有被某个线程占用（也就是记录当前有没有游客已经进入了房子）。最简单的情况是这个state有0和1两个取值，0标识没有线程占用，1标识某个线程占用了这个锁。</p><p>2.如果这个对象被某个线程占用，它得记录这个线程的thread ID,得知道自己是被那个线程占用了（也就是记录现在是谁在房子里面）。</p><p>3.这个对象还得维护一个thread id list，记录其他所有阻塞的、等待拿这个锁的线程（也就是记录所有在外面的等待的游客）。在当前线程释放锁之后（也就是把state从1变为0），从这个thread id list 里面去一个线程唤醒。</p><p>既然锁是一个“对象”，要访问的共享资源本身也是一个对象，例如前面的对象a，这两个对象可以合成一个对象。代码就变成synchronized(this){…}，我们要访问的共享资源是对象a,锁也加在a上面的。当然，也可以另外新建一个对象，代码变成synchronized(obj1){…}。这时访问的共享资源对象是a，而锁是加载新建的对象obj1上面的。</p><p>资源和锁合二为一，使得在Java里面，synchronized关键字可以加在任何对象的成员变量上。这意味着，这个对象既是共享资源，同时也具备“锁”的功能。下面来看Java是如何做到让任何一个对象都具备“锁”的功能的，这也是synchronized的实现原理。</p><h2 id="3-synchronized实现原理"><a href="#3-synchronized实现原理" class="headerlink" title="3.synchronized实现原理"></a>3.synchronized实现原理</h2><p>答案在Java的对象头里。在对象头里，有一个块数据较Mark Word。在64位机器上，<strong>Mark Word</strong>是8个字节（64位）的，这64位中有2个重要字段：锁标志位和占用该锁的thread ID。因为不同版本JVM的实现，对象头的数据结构会有差异，这里不再论述。</p><p>此处值说明锁的实现思路，后面将ReentrantLock详细实现时，也基于类似的思路。在这个思路上，synchronized还会有偏向、自旋等优化策略，ReentrantLock同样也会用到这些优化策略。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-锁的对象是什么？&quot;&gt;&lt;a href=&quot;#1-锁的对象是什么？&quot; class=&quot;headerlink&quot; title=&quot;1.锁的对象是什么？&quot;&gt;&lt;/a&gt;1.锁的对象是什么？&lt;/h2&gt;&lt;p&gt;对于不熟悉多线程原理的人来说，很容易误解synchronized关键字：它通</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数(2)</title>
    <link href="https://www.nesive.com/fafa8499.html"/>
    <id>https://www.nesive.com/fafa8499.html</id>
    <published>2022-04-07T16:26:18.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-什么情况下会考出InterruptedException"><a href="#1-什么情况下会考出InterruptedException" class="headerlink" title="1.什么情况下会考出InterruptedException?"></a>1.什么情况下会考出InterruptedException?</h2><p>Interrupt从字面意思看貌似是说，当一个线程运行到一半，将它中断，然后会抛出InterruptedException异常，然而并不是这样。</p><p>只有声明了会抛出InterruptedException的函数才会抛出异常。如下：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">native</span> <span class="token keyword">void</span> <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token keyword">long</span> millis<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">&#123;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="2-轻量级阻塞与重量级阻塞"><a href="#2-轻量级阻塞与重量级阻塞" class="headerlink" title="2.轻量级阻塞与重量级阻塞"></a>2.轻量级阻塞与重量级阻塞</h2><p>轻量级阻塞：能够被中断的阻塞，对应的线程状态是WAITTING或者TIMED_WAITTING。</p><p>重量级阻塞：不能被中断的阻塞，对应状态是BLOCKED。像synchronized这种。</p><p>如下图，是调用不同函数之后，一个线程完整的状态迁移过程。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_state_transfer.png" alt="Java并发原理-线程的状态迁移过程"></p><p>初始线程处于NEW状态，调用start()之后开始执行，进入RUNNING或者READY状态。如果没有调用任何阻塞函数，线程只会在RUNNING和READY状态切换，即系统的时间片调度。这两种状态切换是由操作系统完成的，开发者基本没有机会介入，除了调用yield()函数，放弃对CPU的占用。</p><p>一旦调用了图中的任何函数，线程就会进入WAITTING或者TIMED_WAITTING状态，两者区别只是前者为无限期阻塞，后者传入了一个时间参数，阻塞了一个有限的时间。如果使用了synchronized关键字或者synchronized块，则会进入BLOCKED状态。</p><p>除了常见的阻塞/唤醒函数，还有一对不太常见的阻塞/唤醒函数,LockSupport.park() 和 LockSupport.unpark()。这对函数非常重要，Concurrent包中的Lock的实现即依赖这一对儿操作原语。</p><p>故而t.interrupted()函数的精确含义是<strong>“唤醒轻量阻塞”</strong>，而不是“中断一个线程”。</p><h2 id="3-t-interrupted-和Thread-interrupted-的区别"><a href="#3-t-interrupted-和Thread-interrupted-的区别" class="headerlink" title="3. t.interrupted()和Thread.interrupted()的区别"></a>3. t.interrupted()和Thread.interrupted()的区别</h2><p>因为t.interrupted()相当于给线程发送了一个唤醒的信号，所以如果线程此时恰好处于WAITTING或者TIMED_WAITTING状态，就会抛出一个InterrutedException,并且线程被唤醒。而如果线程此时没有被阻塞，则线程什么都不会做。但在后续，线程可以判断自己是否收到过其它线程发来的中断信号，然后做一些对应的处理。</p><p>这两个函数都是线程用于判断自己是否收到过中断信号的，前者是非静态函数，后者是静态函数。二者的区别在于，前者只是读取中断状态，不修改状态；后者不仅读取中断状态，还会重置中断标志位。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-什么情况下会考出InterruptedException&quot;&gt;&lt;a href=&quot;#1-什么情况下会考出InterruptedException&quot; class=&quot;headerlink&quot; title=&quot;1.什么情况下会考出InterruptedException?&quot;</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>Java并发实现原理-多线程基础-线程的优雅关闭(1)</title>
    <link href="https://www.nesive.com/7f185260.html"/>
    <id>https://www.nesive.com/7f185260.html</id>
    <published>2022-04-07T15:07:36.000Z</published>
    <updated>2026-11-04T14:29:58.421Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-stop-与destory-函数"><a href="#1-stop-与destory-函数" class="headerlink" title="1.stop()与destory()函数"></a>1.stop()与destory()函数</h2><p>线程是“一种运行中的代码或者函数”。既然是运行中就会涉及到是一个问题，在运行中的线程是否可以强制结束/关闭？</p><p>答案是肯定不可以的。在Java中，stop()和destory()之类的函数是官方明确不建议使用的。原因是如果强制杀死线程，那么线程执行所占用的资源，eg:文件描述符、网络连接不能正常关闭。</p><p>因此，一个线程一旦运行，就不要强制去打断，合理的关闭办法是让它自然运行完（函数执行结束），干净的释放掉所有资源，然后退出。如果是一个不断循环的运行的线程，就需要用到线程的通讯机制，让主线程通知它退出。</p><h2 id="2-守护线程"><a href="#2-守护线程" class="headerlink" title="2.守护线程"></a>2.守护线程</h2><p>在下面代码中：在main()函数中开启一个线程，不断的循环打印。请问：当main()函数退出之后，线程是否会被强制退出？整个进程是否会强制退出？</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"main start..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token class-name">Thread</span> thread1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token punctuation">&#123;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      <span class="token keyword">try</span><span class="token punctuation">&#123;</span>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"thread1 executing.."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token punctuation">&#125;</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token class-name">InterruptedExcetpion</span> ex<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>              <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  thread1<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"main end..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>答案是不会。在C语言中，main()函数退出后，整个程序就退出了，但在Java中并非如此。</p><p>对于如上程序，在thread1.start()前面加一行代码：thread1.setDeamon(true)。当main()函数执行结束后，线程thread1就会退出，整个进程也会退出。</p><p>当在JVM进程里面开启多个线程时，这些线程被分为两类：守护线程与非守护线程。默认开启的是非守护线程。在Java中规定：当所有非守护线程退出后，整个JVM进程就会退出。意思就是守护线程不影响整个JVM退出。eg:垃圾回收线程就是守护线程，他们在后台默默工作，当开发者的所有前台线程（非守护线程）都退出之后，整个JVM进程就退出了。</p><h2 id="3-设置关闭的标志位"><a href="#3-设置关闭的标志位" class="headerlink" title="3.设置关闭的标志位"></a>3.设置关闭的标志位</h2><p>在上面代码中，线程是一个死循环。但在实际工作中，开发人员通常不会这么写，而是通过标志位来实现。如下所示：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">MyThread</span> <span class="token keyword">extends</span> <span class="token class-name">Thread</span><span class="token punctuation">&#123;</span>  <span class="token keyword">private</span> <span class="token keyword">boolean</span> stopped <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span>stopped<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">this</span><span class="token punctuation">.</span>stopped <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token class-name">MyThread</span> t <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  t<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 通知线程t关闭</span>  t<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 等待线程t退出while循环，自行退出</span>  t<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码有一个问题：如过MyThread t在循环中阻塞在某个地方了，例如里面调用了object.wait()函数，那它可能永远没有机会再执行while(!stopped)的代码了，也就一直无法退出循环。</p><p>此时就要用到如下所讲的(<a href="#">Post not found: Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数-2 Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数-2</a>).</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-stop-与destory-函数&quot;&gt;&lt;a href=&quot;#1-stop-与destory-函数&quot; class=&quot;headerlink&quot; title=&quot;1.stop()与destory()函数&quot;&gt;&lt;/a&gt;1.stop()与destory()函数&lt;/h2&gt;&lt;p&gt;线程</summary>
      
    
    
    
    <category term="编程学习" scheme="https://www.nesive.com/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Java并发实现原理" scheme="https://www.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
</feed>
