详解Java安全编码指南之可见性和原子性
可见性问题
在Java中,可见性问题主要是由于多线程之间的共享变量引起的。当一个线程修改了共享变量,这个变量的值可能会被其他线程所看到,也可能不会被看到,这就是可见性问题。Java提供了关键字volatile
和synchronized
来解决可见性问题。
volatile
关键字
volatile
关键字用于修饰共享变量,它保证了多线程之间对共享变量的访问可见性。使用volatile
关键字对于各种开销较小的操作可以提高程序的性能,但是对于一些开销较大的操作,使用volatile
关键字可能会影响程序的性能。
下面是一个示例代码,用于展示volatile
关键字的用法:
public class VolatileDemo {
private volatile int count = 0;
public void increase() {
count++;
}
public static void main(String[] args) throws InterruptedException {
VolatileDemo demo = new VolatileDemo();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(demo.count);
}
}
在上面的代码中,count
变量被声明为volatile
,在increase()
方法中对count
的访问是原子的。创建两个线程来对count
进行修改,最终输出结果为2000。
synchronized
关键字
synchronized
关键字保证了多线程之间对共享变量的访问顺序和可见性。使用synchronized
关键字可以确保同一时间只有一个线程执行锁定代码块中的代码,从而避免了多线程之间的竞争。
下面是一个示例代码,用于展示synchronized
关键字的用法:
import java.util.concurrent.TimeUnit;
public class SynchronizedDemo {
private int count = 0;
public synchronized void increase() {
count++;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo demo = new SynchronizedDemo();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(demo.count);
}
}
在上面的代码中,increase()
方法被声明为synchronized
,在执行该方法时,线程会自动获取该对象的锁定,只有当线程释放锁定后,其他线程才能够继续执行。创建两个线程来对count
进行修改,最终输出结果为2000。
原子性问题
在Java中,原子性问题主要是由于多线程之间进行共享变量的读写操作引起的。当多个线程同时对同一个变量进行读写操作时,就可能会出现原子性问题。Java提供了java.util.concurrent.atomic
包来解决原子性问题。
java.util.concurrent.atomic
包
java.util.concurrent.atomic
包提供了一些原子性的类,比如AtomicBoolean
、AtomicInteger
、AtomicLong
等。这些类提供了一些原子性的方法,比如getAndIncrement()
、getAndSet()
、compareAndSet()
等。
下面是一个示例代码,用于展示java.util.concurrent.atomic
包的用法:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo {
private AtomicInteger count = new AtomicInteger(0);
public void increase() {
count.getAndIncrement();
}
public static void main(String[] args) throws InterruptedException {
AtomicDemo demo = new AtomicDemo();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
demo.increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(demo.count.get());
}
}
在上面的代码中,count
变量被声明为AtomicInteger
,在increase()
方法中对count
的访问是原子的。创建两个线程来对count
进行修改,最终输出结果为2000。
总结
可见性和原子性是Java中的两个重要问题,可以使用volatile
、synchronized
和java.util.concurrent.atomic
包来解决这些问题。在代码编写时,需要注意线程之间的竞争关系,保证代码的正确性和代码的可读性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解java安全编码指南之可见性和原子性 - Python技术站