看看这段代码:有两个线程一个对count++一万次,另一个对count–一万次。结果应该还是0。
public class Test1 {
private static int count;
public static void main(String[] args) throws InterruptedException {
Test1 t = new Test1();
Thread t1 = new Thread(t::addCount, "t1");
Thread t2 = new Thread(t::subCount, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++)
count++;
System.out.println(Thread.currentThread().getName() + "结束");
}
public void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++)
count--;
System.out.println(Thread.currentThread().getName() + "结束");
}
}
运行结果:
t1启动
t2启动
t1结束
t2结束
count=-974
其实运行结果每次都不一样,原因是++操作不具备原子性。表面好像只有把数加一的过程但是底层上会先读取这个数,在使这个数加一,然后再写入新的结果。如果在还没有写入的时候其他线程先完成了这一系列操作,那么便会产生脏数据。
有什么办法可以解决这一问题呢?很简单。如果让这一过程具有原子性就行了呗。
java中提供了synchronized锁,该锁会锁定堆上对象。每次只允许一个线程访问。不过这样的监控势必会浪费资源,所以尽量减少被锁定的区域。
看看下面的代码,生成了一个Object对象。在++和–操作时都会被锁定住。
public class Test2 {
private static int count = 0;
private Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Test2 t = new Test2();
Thread t1 = new Thread(t::addCount, "t1");
Thread t2 = new Thread(t::subCount, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
synchronized (lock) {
count++;
}
}
System.out.println(Thread.currentThread().getName() + "结束");
}
public void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
synchronized (lock) {
count--;
}
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
但是每次new一个新的对象烦不烦啊。所以也可以锁定this。谁调用我我就锁定谁。
public class Test3 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Test3 t = new Test3();
Thread t1 = new Thread(t::addCount, "t1");
Thread t2 = new Thread(t::subCount, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
synchronized (this) {
count++;
}
}
System.out.println(Thread.currentThread().getName() + "结束");
}
public void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
synchronized (this) {
count--;
}
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
当然该锁也可以锁定一个方法。也是谁调用我我就锁定谁。
public class Test4 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Test4 t = new Test4();
Thread t1 = new Thread(t::addCount, "t1");
Thread t2 = new Thread(t::subCount, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public synchronized void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
count++;
}
System.out.println(Thread.currentThread().getName() + "结束");
}
public synchronized void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
count--;
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
当然该锁除了锁定普通方法还可以锁定静态方法。即该类的类对象。
public class Test5 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{addCount();}, "t1");
Thread t2 = new Thread(()->{subCount();}, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public synchronized static void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
count++;
}
System.out.println(Thread.currentThread().getName() + "结束");
}
public synchronized static void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++) {
count--;
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
不过这把锁有些重当做简单的诸如++操作的时候有更简单的原子类可以用。AtomicInteger就是一个原子类
public class Test6 {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Test6 t = new Test6();
Thread t1 = new Thread(t::addCount, "t1");
Thread t2 = new Thread(t::subCount, "t2");
t1.start();
t2.start();
Thread.sleep(3000);
System.out.println("count=" + count);
}
public synchronized void addCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++)
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + "结束");
}
public synchronized void subCount() {
System.out.println(Thread.currentThread().getName() + "启动");
for (int i = 0; i < 10000; i++)
count.decrementAndGet();
System.out.println(Thread.currentThread().getName() + "结束");
}
}
后面例子的运行结果:
t1启动
t1结束
t2启动
t2结束
count=0