首先我们来看一下这个代码:
public class Main implements Runnable {
private static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Main());
thread.start();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("result: " + count);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++)
count++;
}
}
运行结果如下:
开启了10个线程,每个线程都累加了10000次,如果结果正确的话自然而然总数就应该是10 * 10000 = 100000。可就运行多次结果都不是这个数,而且每次运行结果都不一样。这是为什么呢?有什么解决方案呢?
解决方案如下:
使用Java synchronized 关键字
synchronized ,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍可以访问该object中的非加锁代码块。
如图,synchronized可以用在方法上也可以使用在代码块中,其中方法是实例方法和静态方法分别锁的是该类的实例对象和该类的对象。而使用在代码块中也可以分为三种,具体的可以看上面的表格。这里的需要注意的是:如果锁的是类对象的话,尽管new多个实例对象,但他们仍然是属于同一个类依然会被锁住,即线程之间保证同步关系。
public class Main implements Runnable {
private static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Main());
thread.start();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("result: " + count);
}
@Override
public void run() {
synchronized (Main.class) {
for (int i = 0; i < 10000; i++)
count++;
}
}
}
运行结果:
现在不管运行几次,结果总数都是10 * 10000 = 100000,好了问题解决。