1.指定加锁的对象,进入同步代码前获得给定实例对象的锁;
对于这段代码,我们要注意的是,如果加锁的是i而不是实例对象instance,那么会得到不一样的数,因为这段程序并非真正的线程安全,那是因为Integer.valueOf本质上是工厂方法,倾向于返回一个代表指定数值的Integer实例,i++本质是创建一个新的Integer对象,并将它的引用赋值给i,因此,多个线程间并不一定能够看到同一个i对象,两个进程加锁就加到了不同的对象实例上,从而导致临界区代码控制出现问题;
public class AccountingVol implements Runnable{
static AccountingVol instance = new AccountingVol();
static volatile int i = 0;
@Override
public void run() {
for(int j = 0;j< 100000000;j++) {
synchronized(instance) {i++;};
}
}
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new AccountingVol());
Thread t2 = new Thread(new AccountingVol());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
2.直接用于实例方法:相当于对当前实例加锁,进入同步代码前需要获得当前实例的锁;
public class AccountingVol implements Runnable{
static AccountingVol instance = new AccountingVol();
static int i = 0;
public synchronized void increase() {
i++;
}
@Override
public void run() {
for(int j = 0;j< 100000000;j++) {
increase();
}
}
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
注意!!!!!!!!!!!!!!!!!
上述代码中,在进入increase方法前,线程必须获得当前对象实例的锁,本例中就是instance对象,这里使用Runnable接口来创建线程,要求两个线程都指向同一个Runnable接口实例(instance对象),这样才保证两个线程在工作时,能够关注到同一个对象锁上,从而保证线程安全;
3.直接作用于静态方法:相当于给当前类加锁,进入同步代码前需要获得当前类的锁;
public class AccountingVol implements Runnable{
static int i = 0;
public static synchronized void increase() {
i++;
}
@Override
public void run() {
for(int j = 0;j< 100000000;j++) {
increase();
}
}
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new AccountingVol());
Thread t2 = new Thread(new AccountingVol());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}