synchronized同步方法
“非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是取到的数据其实是被更改过的。
“线程安全”就是已获得的实例变量的值是经过同步处理的,不会出现脏读的现象。
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的了,这是方法内部的变量是私有的特性造成的。
package other;
/**
* 线程不安全
* @author xml
*
*/
public class NoThreadSafe {
private static String userName;
private static String passWard;
/**
* 模拟Selvet
*/
public synchronized static void doPost(String userNameRef,String passWardRef) {
try {
userName = userNameRef;
if(userName.equals("aaa")) {
Thread.sleep(1000);
}
passWard = passWardRef;
System.out.println("userName:" + userName + "----" + "passWard:" + passWard);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadTestA extends Thread{
@Override
public void run() {
NoThreadSafe.doPost("aaa", "aaa");
}
}
public class ThreadTestB extends Thread{
@Override
public void run() {
NoThreadSafe.doPost("bbb", "bbb");
}
}
public class Test {
public static void main(String[] args) {
ThreadTestA aaa = new ThreadTestA();
aaa.start();
ThreadTestB bbb = new ThreadTestB();
bbb.start();
}
}
结论:在两个线程访问同一个对象中的同步方法时一定是线程安全的。
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
多个线程调用同一个方法时,为了避免数据出现交叉情况,使用synchronized关键字来进行同步。
虽然在赋值时进行了同步,但是在取值的时候有可能会出现脏读的情况,其原因是在读取实例变量时,此值已经被其他线程更改过了。
package other.thread2;
public class PublicBean {
private String userName = "A";
private String passWord = "AA";
public synchronized void setValue(String userName,String passWord) {
try {
this.userName = userName;
Thread.sleep(5000);
this.passWord = passWord;
System.out.println("setValue method name " + Thread.currentThread().getName()
+ "---userName:" + userName + "---pwd:" + passWord);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue() {
System.out.println("getValue method name " + Thread.currentThread().getName()
+ "---userName:" + userName + "---pwd:" + passWord);
}
}
public class ThreadA extends Thread {
private PublicBean bean;
public ThreadA(PublicBean bean) {
this.bean = bean;
}
@Override
public void run() {
bean.setValue("B", "BB");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
PublicBean bean = new PublicBean();
ThreadA threadA = new ThreadA(bean);
threadA.start();
Thread.sleep(200);
bean.getValue();
}
}
image.png
出现脏读是因为public void getValue()方法不是同步的,所以可以在任意的时候调用。解决的办法就是加上同步关键字synchronized。
加完后结果如下:
image.png
可见setValue和getValue被依次执行。
总结:当A线程调用anyObj对象加入synchronized关键字的X方法时,A线程就获得了X对象锁,所以其他线程必须等A线程执行完毕后才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。
除此之外,关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以在的得到该对象的锁的,这也证明在一个synchronized方法/块的内部调用本类其他synchronized方法/块时,是永远可以得到锁的。