synchronized同步方法是为了解决“非线程安全”的问题,所谓“非线程安全”是指多个线程对同一个对象中的变量进行并发访问时发生,产生“脏读”的情况。
一、方法内的变量为线程安全
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,也就是“线程安全”的了。
demo:
HasSelfPrivateNum类:
public class HasSelfPrivateNum {
public void addI(String username){
try{
int num=0;
if(username.equals("a")){
num=100;
System.out.println("a set over!");
Thread.sleep(2000);
}else{
num=200;
System.out.println("b set over!");
}
System.out.println(username+" num="+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
A线程类
public class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA (HasSelfPrivateNum numRef){
super();
this.numRef=numRef;
}
@Override
public void run(){
super.run();
numRef.addI("a");
}
}
B线程类:
public class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB (HasSelfPrivateNum numRef){
super();
this.numRef=numRef;
}
@Override
public void run(){
super.run();
numRef.addI("b");
}
}
Main方法:
public class Run {
public static void main(String[] args){
HasSelfPrivateNum numRef=new HasSelfPrivateNum();
ThreadA athread=new ThreadA(numRef);
athread.start();
ThreadB bthread=new ThreadB(numRef);
bthread.start();
}
}
运行结果:
a set over!
b set over!
b num=200
a num=100
从结果可以看出这是线程安全的且永久安全,这是方法内部的变量私有的特性的原因(num变量是属于方法内部的)。
二、实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”的问题;
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况;
如果对象仅有1个实例变量,则有可能出现覆盖的情况。
修改HasSelfPrivateNum类,如下:
public class HasSelfPrivateNum {
private int num=0;
public void addI(String username){
try{
if(username.equals("a")){
num=100;
System.out.println("a set over!");
Thread.sleep(2000);
}else{
num=200;
System.out.println("b set over!");
}
System.out.println(username+" num="+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行Main方法,结果如下:
a set over!
b set over!
b num=200
a num=200
本实例中两个线程同时操作业务对象中的实例变量,产生了覆盖的情况,出现了“线程不安全”的问题。
解决方法:
在public void addI(String username)方法钱加关键字synchronized即可,如下:
public class HasSelfPrivateNum {
private int num=0;
synchronized public void addI(String username){
try{
if(username.equals("a")){
num=100;
System.out.println("a set over!");
Thread.sleep(2000);
}else{
num=200;
System.out.println("b set over!");
}
System.out.println(username+" num="+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行main方法,结果如下:
a set over!
a num=100
b set over!
b num=200
当多个线程访问同一个对象中的同步方法时一定是线程安全的。
因为是同步访问,所以先打印a,然后打印出b。