Spring - JUC synchronized关键字详解

论坛 期权论坛     
选择匿名的用户   2021-5-31 08:44   139   0
<h1>引入</h1>
<p>编写一个类似银行、医院的叫号程序(要求:多个窗口叫号,不重号、不跳号)</p>
<p><img alt="" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-9c9d4247c60e638fe13d06eaad1bf55a.png"></p>
<p> </p>
<p>这个用到多线程来实现多个窗口叫号的功能,首先要解决的就是资源共享问题,因为不同线程(不同窗口)所使用的叫号计数器应该是同一个,否则就会出现重号的问题。</p>
<p><strong>资源共享的解决方案有两种:</strong></p>
<ul><li>使用static关键字修饰要共享的变量,将其变为全局静态变量,也就是放到了JMM的主内存中,这要就实现了资源的共享。</li><li>实现Runnable接口,这个接口和Thread类的区别之一就是可以实现资源的共享,因为实现Runnable接口的线程所操作的资源对象本质是是同一个对象</li></ul>
<p>解决完资源共享问题之后,还有一个新的问题,那就是并发量比较大的时候会出现:跳号、重号、超过最大值。因为在加号过程中对index的增加操作在工作空间,就需要将index从主内存复制到工作空间进行操作,操作完再更新主内存中的index数据。很明显这不是一个原子操作。这就造成了有可能线程1正在对index进行操作,还没有操作完线程2也对index进行操作,线程2无法感知index已经被更新,这就不符合原子性和可见性原则。这就需要使用到<strong>synchronized</strong><strong>关键字</strong>来保证原子性和可见性。</p>
<p><strong>代码:</strong></p>
<pre class="blockcode"><code class="language-java">public class  TicketDemo extends Thread{
    private static int index&#61;1;//
    private static final int MAX&#61;5000;
    &#64;Override
    public void run() {
        synchronized (this){
            while(index&lt;&#61;MAX){
                System.out.println(Thread.currentThread().getName()&#43;&#34;叫到号码是:&#34;&#43;(index&#43;&#43;));
            }
        }
    }
    public static void main(String[] args) {
        TicketDemo t1&#61;new TicketDemo();
        TicketDemo t2&#61;new TicketDemo();
        TicketDemo t3&#61;new TicketDemo();
        TicketDemo t4&#61;new TicketDemo();
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}</code></pre>
<p>如果某一个资源被多个线程共享,为了避免因为资源抢占导致资源数据错乱,我们需要对线程进行同步,那么synchronized就是实现线程同步的关键字,可以说在并发控制中是必不可少的部分,今天就来看一下synchronized的使用和底层原理。可以通过synchronized关键字实现互斥同步,进而来实现线程安全。</p>
<p> </p>
<h1 id="%E4%B8%80%E3%80%81synchronized%E7%9A%84%E7%89%B9%E6%80%A7"><strong>一、</strong><strong>synchronized</strong><strong>的特性</strong></h1>
<p><strong>synchronized</strong>是利用<strong>锁的机制</strong>来实现同步的。下面synchronized的特性也就是该关键字在并发编程中能保证的特性。</p>
<h2 id="1.1%20%E5%8E%9F%E5%AD%90%E6%80%A7"><a name="t2"></a><strong>1.1 原子性</strong></h2>
<p><strong>所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。</strong></p>
<p>在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。但是像i&#43;&#43;、i&#43;&#61;1等操作字符就不是原子性的,它们是分成<strong>读取、计算、赋值</strong>几步操作,原值在这些步骤还没完成时就可能已经被赋值了,那么最后赋值写入的数据就是脏数据,无法保证原子性。</p>
<p>被synchronized修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的锁,直到执行完才能释放,这中间的过程无法被中断(除了已经废弃的stop()方法),即保证了原子性。</p>
<p><strong>注意!面试时经常会问比较synchronized和volatile,它们俩特性上最大的区别就在于原子性,</strong><strong>volatile不具备原子性</strong><strong>。</strong></p>
<h2><a name="t3"></a> </h2>
<h2 id="1.2%20%E5%8F%AF%E8%A7%81%E6%80%A7"><a name="t4"></a><strong>1.2 可见性</strong></h2>
<p><strong>可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。</strong></p>
<p><strong>synchronized和volatile都具有可见性</strong>,其中synchronized对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而<u>这个锁的状态对于其他任何线程都是可见的,并且在</u><strong><u>释放锁之前</u></strong><u>会将对变量的修改刷新到主存当中</u>,保证资源变量
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP