java中多线程的同步分析

来源:互联网 发布:天刀鹿晗捏脸数据 编辑:程序博客网 时间:2024/04/29 09:49

多线程的出现提高了CPU的利用率,也提供给了我们更多创造好玩的游戏、软件的思路和方法。但是另一方面,在使用多线程的时候,我们要格外注意多线程带来了潜在的不安全性,本文先介绍一下线程中最基本的同步问题。

在使用多线程的时候,我们往往会采用实现Runnable接口的方式创建一个类,这时实现的run()方法中的代码则是多个线程所共用的,这些代码中也可能会有共享的数据,当代码语句很多时,对于里面某些需要安全判断的语句来说,CPU的切换很可能会造成一种安全问题。比如代码里面有一个判断语句

if (flag > 0) {flag--;System.out.println(flag);}
就单线程模式来讲,输出的flag是不可能出现负数的情况的,然而一旦涉及到多线程,像上面说的,多个线程里面都含有这些代码,我们假使当flag=1的时候,这时一个线程进入这个判断,这时是满足flag>0的条件的,然而当线程进入语句块但是还没有执行flag--这行代码的时候,CPU一下子将执行权给了另外一个线程,注意,这个时候flag还是等于1,所以另外一个线程顺利进入了if语句块。不用多说,其他别的线程也能这样进来,这样,当运行输出语句的时候,flag的值早就是一个负数了。(当然,因为线程执行这几句语句的速度非常快,所以出现这个问题的概率也很低,不过我们可以在flag--之前写一句Thread.sleep(50),让线程停一会,这样线程也就更容易卡在这个地方了)

了解到线程中潜在的安全问题后,我们就要采取java中提供给我们的处理安全问题的方法了,java中为了解决多线程的安全问题,提供了一种方法:同步。

同步的关键字是synchronized,同步语句块的一般形式是

synchronized ( /*对象锁*/ ) {//需要被同步的代码}
其中对象锁是一个对象,也就是API文档中说的对象监视器,进入代码块的线程需要拥有此对象监视器。比如现在程序中有两个线程正在同时运行,且两个线程都即将要执行同步语句块,这时还没有哪一个线程拿到这个对象锁,当其中一个线程进入这个语句块的时候,此线程将首先拿到锁,在它执行同步代码块里面的代码的时候,即使CPU将执行权切到其他线程,其他线程也会因为没有拿到锁而进不去同步代码块。而当拿到锁的那个线程离开此代码块时将释放对象锁,此时其他线程才能进入同步代码块。

刚开始接触synchronized可能会纠结写哪一个对象锁。其实对象锁是可以任意选择的,甚至可以new一个Object当作这个锁,但是需要注意的是必须要保证多个线程共用这个对象锁,比如

public void run() {Object obj = new Object();synchronized(obj){//代码}}
因为每个线程都有自己的run方法,所以每个线程进入自己的run方法的时候会拥有自己的obj,这样synchronized就显得毫无意义。

当然也可以在程序中写多个同步代码块,当同步代码块的对象锁是同一个锁的时候,则可以把这些代码块看成是一体的,因为无论执行哪一个代码块,其他线程将无法进入其它的代码块。

另外synchronized关键字也可以写在函数名的前面,这时这个函数将是一个同步函数,函数可以看作是一个大的同步代码块,此代码块同步锁是this。其实是很好理解的,因为当程序简单到只有一个类的时候,也就只有一个能共享的对象即this,而且函数的调用者就是this。那么,大家可能会想到如果这个函数是静态的,那么函数的锁又是什么呢?此时,这个锁是此类的字节码文件对象,可以通过类名.class或this.getClass()得到,这也是一个类独一无二的对象。

为什么要出现同步呢,综上所述,个人感觉同步就好像是一个“保护罩”一样的,将可能潜在安全问题的代码保护起来,只允许一个线程去访问,这样避免多线程同时访问一个数据而出现安全问题。

0 0
原创粉丝点击