《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 周志明》 - 第12章代码示例

发布时间:2024年01月18日

背景

? ? ? ? 最近在看《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 周志明》这本书,书中有些代码示例是为了让读者理解作者表达的意思,但不是完整的代码示例,所以针对这些不完整的代码,自己动手写出完整的代码示例。

说明

(1)在看这本书的同学,可以拿我这里的示例代码做个参考,并欢迎提出意见和建议;

(2)示例的标号,是和书中的代码示例标号相对应的。

第12章 -?代码

示例:12-1

/**
 * 这个程序演示的是:《深入理解JVM》(周志明 第三版) 代码清党12-1 volatile运算
 * 
 * volatile关键字只能保证可见性,并不能保证原子性(和有序性)
 * 因为程序输出的结果有不等于200000的情况发生
 */
public class VolatileTest_12_1 {


    public static volatile int race = 0;


    public static void increase() {
        race++;
    }

    private static final int THREAD_COUNT = 20;

    public static void main(String[] args) {
        Thread[] threads = new Thread[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        increase();
                    }
                }
                    
            });
            threads[i].start();
        }

        // 等待所有累加线程都结束
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println(race);
    }
}

示例:12-1的解决方式一

/**
 * 这个程序演示的是:
 *
 * volatile关键字只能保证可见性,并不能保证原子性(和有序性)
 * 
 * 针对这个程序,想要保证并发执行的结果符合预期,可以使用 synchronized关键字实现(但是这种实现方式是重量级的实现,效率较低)
 */
public class VolatileTest_12_1_Solution_1 {

    public static volatile int race = 0;
    
    public static void increase() {
        race++;
    }

    private static final int THREAD_COUNT = 20;

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Thread[] threads = new Thread[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (VolatileTest_12_1_Solution_1.class) {
                        for (int i = 0; i < 10000; i++) {
                            increase();
                        }
                    }

                }
            });
            threads[i].start();
        }

        // 等待所有累加线程都结束
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(race);
        long end = System.currentTimeMillis();
        System.out.println((end - start));
    }
}

示例:12-1的解决方式二

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 这个程序演示的是:
 * <p>
 * volatile关键字只能保证可见性,并不能保证原子性(和有序性)
 * <p>
 * 针对这个程序,想要保证并发执行的结果符合预期,可以使用 synchronized关键字实现(但是这种实现方式是重量级的实现,效率较低)
 */
public class VolatileTest_12_1_Solution_2 {

    //public static volatile int race = 0;

    //public static void increase() {
    //    race++;
    //}

    public static AtomicInteger race = new AtomicInteger(0);

    public static void increase() {
        race.incrementAndGet(); // 原子操作的方法(底层使用CAS指令实现)
    }

    private static final int THREAD_COUNT = 20;

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Thread[] threads = new Thread[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        increase();
                    }
                }
            });
            threads[i].start();
        }

        // 等待所有累加线程都结束
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(race);
        long end = System.currentTimeMillis();
        System.out.println((end - start));
    }
}

示例:12-3

/**
 * 
 * volatile的使用场景
 * 
 * (书中原话)
 * 而在像代码清单12-3所示的这类场景中就很适合使用volatile变量来控制并发,当shutdown()方法被调用时,
 * 能保证所有线程中执行的doWork()方法都立即停下来。
 */
public class VolatileTest_12_3 {

    private static final int THREADS_COUNT = 3;

    public static void main(String[] args) throws InterruptedException {

        MyTask myTask = new MyTask();
        Thread[] threads = new Thread[THREADS_COUNT];
        for (int i = 0; i < THREADS_COUNT; i++) {
            threads[i] = new Thread(myTask, "t"+(i+1));
            threads[i].start();
        }
        Thread.sleep(500);
        myTask.shutdown();
    }
}


class MyTask implements Runnable {

    private volatile boolean shutdownRequested;

    public void shutdown() {
        System.out.println(Thread.currentThread().getName()+"---->");
        shutdownRequested = true;
    }

    @Override
    public void run() {
        while (!shutdownRequested) {
            //try {
            //    Thread.sleep(500);
            //} catch (InterruptedException e) {
            //    throw new RuntimeException(e);
            //}
            System.out.println(Thread.currentThread().getName() + " --- do something...");
        }
    }
}

示例:12-4

import lombok.extern.slf4j.Slf4j;

/**
 * 需求:模拟一个A线程用来读取配置信息,另一个B线程等待A线程把配置信息读取完成后执行后续任务,
 * 其中线程A在读取完成配置信息后会把一个flag标志位由false变为true, B线程读取到这个flag标志位为true后,才开始执行
 */
@Slf4j
public class VolatileTest_12_4 {
    
    public static void main(String[] args) {
        
        // 创建配置信息对象
        Configuration configuration = new Configuration();
        
        Thread threadA = new Thread("Thread-A"){
            @Override
            public void run() {
                // 模拟读取配置信息的耗时操作
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 读取完配置信息后,将其标志位改为true
                configuration.setConfigLoaded(true);
                log.info("Thread-A: Configuration reading completed.");          
            }
        };

        Thread threadB = new Thread("Thread-B"){
            @Override
            public void run() {
                // 等待标志位为true
                while (!configuration.isConfigLoaded()){
                    // 在标志位为false时,线程Thread-B什么都不做
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    log.info("no no no no...");
                }
                
                log.info("Thread-B: Start executing subsequent tasks.");
            }
        };
        
        threadA.start();
        threadB.start();
    }
}

// 配置类
class  Configuration{
    private volatile boolean configLoaded = false;

    public boolean isConfigLoaded() {
        return configLoaded;
    }

    public void setConfigLoaded(boolean configLoaded) {
        this.configLoaded = configLoaded;
    }
}

未完待续...

文章来源:https://blog.csdn.net/qq_23388169/article/details/135665502
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。