- `volatile`关键字有两个作用:保证可见性和禁止指令重排序。 - 保证可见性:当一个变量被`volatile`修饰时,它会被保证对所有线程的可见性。也就是说,当一个线程修改了这个变量的值,其他线程可以立即看到修改后的值,而不是使用缓存中的旧值。 - 禁止指令重排序:当代码执行时,JVM为了优化,可能会对指令进行重排序。而有时候,这种重排序可能会导致程序出现错误。当一个变量被`volatile`修饰后,JVM会禁止对其进行指令重排序,从而保证程序的正确性。 - 举例:假设有两个线程,一个线程负责写入变量,另一个线程负责读取变量。如果没有使用`volatile`关键字修饰变量,那么读取线程可能会一直读取缓存中的旧值,而写入线程可能会将新值一直保存在CPU的寄存器中,不会及时刷回内存。但是,如果使用`volatile`关键字修饰变量,那么写入线程修改变量的值后,会立即刷回到内存,而读取线程读取变量时,会从内存中获取最新的值,从而保证了可见性。
- `synchronized`关键字用于实现多线程之间的同步。通过加锁和释放锁的机制,确保在同一时间只有一个线程可以访问被`Synchronized`修饰的方法或代码块。 - 根据使用方式的不同,可以分成两种情况: - 对象锁:当`synchronized`修饰一个普通方法或代码块时,使用的是对象级别的锁。在同一时间内,只有一个线程可以访问这个对象的被`Synchronized`修饰的方法或代码块。 - 类锁:当`synchronized`修饰一个静态方法或代码块时,使用的是类级别的锁。在同一时间内,只有一个线程可以访问这个类的被`Synchronized`修饰的静态方法或代码块。 - 举例:假设有一个共享资源`counter`和两个线程同时对其进行操作。如果不使用`synchronized`关键字进行同步,可能会导致并发问题,如数据不一致。但是,如果使用`Synchronized`关键字对操作`counter`的方法或代码块进行同步,可以确保在任意时间点只有一个线程在操作`counter`,从而避免了并发问题。
public class Counter { private int counter = 0; public synchronized void increment() { counter++; } public synchronized void decrement() { counter--; } public int getCounter() { return counter; } } public class Main { public static void main(String[] args) { Counter counter = new Counter(); Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { counter.decrement(); } }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter.getCounter()); // 输出结果为0 } }
这个例子中,counter
是一个共享资源,初始值为0。线程t1
负责递增counter
的值,线程t2
负责递减counter
的值。通过synchronized
关键字对increment()
和decrement()
方法进行同步,保证在任意时间点只有一个线程可以访问这两个方法,避免了并发问题。最终输出结果为0。