java多线程总结四:volatile、synchronized示例

时间:2023-03-09 17:14:52
java多线程总结四:volatile、synchronized示例

1、synchronized保证同步

先看一个生成偶数的类

  1. <span style="font-size:16px;">package demo.thread;
  2. /**
  3. *这是一个int生成器的抽象类
  4. *
  5. */
  6. public abstract class IntGenerator {
  7. private volatile boolean canceled = false;
  8. public abstract int next();
  9. public void cancel() {
  10. canceled = true;
  11. }
  12. public boolean isCanceled() {
  13. return canceled;
  14. }
  15. }
  16. </span>
  1. <span style="font-size:16px;">/*
  2. * 产生偶数
  3. */
  4. class EvenGenerator extends IntGenerator {
  5. private int currentEvenValue = 0;
  6. String s = "";
  7. @Override
  8. public int next() {
  9. <span style="color:#ff0000;">synchronized </span>(s) {
  10. ++currentEvenValue;
  11. ++currentEvenValue;
  12. return currentEvenValue;
  13. }
  14. }
  15. //  //这样也可以
  16. //  public <span style="color:#ff0000;">synchronized </span>int next() {
  17. //          ++currentEvenValue;
  18. //          ++currentEvenValue;
  19. //          return currentEvenValue;
  20. //  }
  21. }</span>

注意到在产生偶数是要加同步锁,否则可能线程1刚好执行了一句++currentEvenValue;操作,就被线程2抢去了cpu,此时线程2执行return currentEvenValue;这时返回的就是一个奇数。加synchronized
就是两个线程同时只能一个线程执行synchronized 块的代码。

测试代码:

  1. <span style="font-size:16px;">package demo.thread;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. /*
  5. * 消费数字
  6. */
  7. public class EvenChecker implements Runnable {
  8. private IntGenerator generator;
  9. private final int id;
  10. public EvenChecker(IntGenerator g, int ident) {
  11. generator = g;
  12. id = ident;
  13. }
  14. public void run() {
  15. while (!generator.isCanceled()) {
  16. int val = generator.next();
  17. if (val % 2 != 0) {//如果不是偶数
  18. System.out.println(val + " not enen!");
  19. generator.cancel();
  20. }
  21. }
  22. }
  23. public static void test(IntGenerator gp, int count) {
  24. ExecutorService exec = Executors.newCachedThreadPool();
  25. for (int i = 0; i < count; i++)
  26. exec.execute(new EvenChecker(gp, i));
  27. exec.shutdown();
  28. }
  29. public static void test(IntGenerator gp) {
  30. test(gp, 10);
  31. }
  32. public static void main(String[] args) {
  33. test(new EvenGenerator());
  34. }
  35. }</span>

分析:如果产生偶数的类未加synchronized,那么测试程序将会出现奇数导致退出程序。

2、volatile表示原子性,可见性。

对于多个线程之间共享的变量,每个线程都有自己的一份拷贝,当线程1改变变量值时,其他线程并不马上知道该变量值改变了,volatile就保证了变量值对各个线程可见,一个线程改变该值,马上其他线程中该值也改变。原子性表明操作不可中断,如基本变量赋值。

代码示例:

  1. <span style="font-size:16px;">package demo.thread;
  2. public class VolatileDemo implements Runnable {
  3. private volatile int i = 0;//volatile设置可见性
  4. public synchronized  int getValue() {
  5. return i;
  6. }
  7. private synchronized void enenIncrement() {
  8. i++;
  9. i++;
  10. }
  11. @Override
  12. public void run() {
  13. while (true)
  14. enenIncrement();
  15. }
  16. public static void main(String[] args) {
  17. VolatileDemo at = new VolatileDemo();
  18. new Thread(at).start();
  19. while (true) {
  20. int val = at.getValue();
  21. if (val % 2 != 0) {//出现奇数,退出程序
  22. System.out.println(val+" is not enen!");
  23. System.exit(0);
  24. }
  25. }
  26. }
  27. }
  28. </span>

注意i++操作并不是原子行操作,getValue() 方法也要加synchronized 。