java多线程编程之Synchronized关键字详解
本文介绍java多线程中的synchronized关键字作为对象锁的一些知识点。
所谓对象锁,就是就是synchronized 给某个对象 加锁。关于 对象锁 可参考:这篇文章
一、分析
synchronized可以修饰实例方法,如下形式:
public class myobject { synchronized public void methoda() { //do something.... }
这里,synchronized 关键字锁住的是当前对象。这也是称为对象锁的原因。
为啥锁住当前对象?因为 methoda()是个实例方法,要想执行methoda(),需要以 对象.方法() 的形式进行调用(obj.methoda(),obj是myobject类的一个对象,synchronized就是把obj这个对象加锁了)。
上面代码也可写成这样:
public class myobject { public void methoda() { synchronized(this){ //do something.... } }
二、特点
使用synchronized关键字同步一个明显的特点是:myobject类中定义有多个synchronized修饰的实例方法时,若多个线程拥有同一个myobject类的对象,则这些方法只能以同步的方式执行。即,执行完一个synchronized修饰的方法后,才能执行另一个synchronized修饰的方法。
如下:
public class myobject { synchronized public void methoda() { //do something.... } synchronized public void methodb() { //do some other thing } }
myobject类中有两个synchronized修饰的方法。
public class threada extends thread { private myobject object; //省略构造方法 @override public void run() { super.run(); object.methoda(); } }
线程a执行methoda()
public class threadb extends thread { private myobject object; //省略构造方法 @override public void run() { super.run(); object.methodb(); } }
线程b执行methodb()
public class run { public static void main(string[] args) { myobject object = new myobject(); //线程a与线程b 持有的是同一个对象:object threada a = new threada(object); threadb b = new threadb(object); a.start(); b.start(); } }
由于线程a和线程b持有同一个myobject类的对象object,尽管这两个线程需要调用不同的方法,但是必须是同步的,比如:线程b需要等待线程a执行完了methoda()方法之后,它才能执行methodb()方法。
三、结论
从上可以看出,本文中讲述的 synchronized 锁的范围是整个对象。如果一个类中有多个synchronized修饰的同步方法,且多个线程持有该类的同一个对象(该类的相同的对象),尽管它们调用不同的方法,各个方法的执行也是同步的。
如果各个同步的方法之间没有共享变量,或者说各个方法之间没有联系,但也只能同步执行,这会影响效率。
四、应用--使用synchronized避免 因数据不一致性而导致读脏数据的情况
如下示例:
public class myobject { private string username = "b"; private string password = "bb"; synchronized public void methoda(string username, string password) { this.username = username; try{ thread.sleep(5000); }catch(interruptedexception e){ } this.password = password; } synchronized public void methodb() { system.out.println("username" + username + ": " + "password" + password); } }
methoda()负责更改用户名和密码。在现实中,一个用户名对应着一个密码。
methodb()负责读取用户名和密码。
如果methodb()没有用synchronized 修饰,线程a在调用methoda()执行到第7行,更改了用户名,因某种原因(比如在第9行睡眠了)放弃了cpu。
此时,如果线程b去执行methodb(),那么读取到的用户名是线程a更改了的用户名("a"),但是密码却是原来的密码("bb")。因为,线程a睡眠了,还没有来得及更改密码。
但是,如果methodb()用synchronized修饰,那么线程b只能等待线程a执行完毕之后(即改了用户名,也改了密码),才能执行methodb读取用户名和密码。因此,就避免了数据的不一致性而导致的脏读问题。
以上就是本文的全部内容,希望对大家学习java程序设计有所帮助。