Skip to main content

说说并发编程 volatile

Java volatile关键字是用来保证变量的线程可见性。到底什么是线程的可见性呢?准确地讲,每次读取volatile变量要从主内从中读取,而不是从CPU cache中读取;写一个volatile变量,要直接写到主内存,而不仅仅是CPU cache。

Java 5 引入了volatile,就保证了读写都要从主内存中取值。下边详细介绍一下:

线程可见性
在多线程应用程序中, 当线程操作非volatile变量,每个线程需要复制变量从主内存到 CPU缓存中,出于性能方面的设计。如果你的计算机包含多个 CPU,每个线程可能会在不同的 CPU 上运行。这意味着,每个线程可能会将变量复制到不同的 Cpu 的 CPU 缓存。如下所示 ︰
字节码

非volatile变量不保证从主内存中立即读取数据或将数据从CPU 缓存中直接写入到主内存。如果多线程操作一个共享变量的话,就会出现如下的问题:

[java]
public class SharedObject {

public volatile int counter = 0;

}

[/java]

想象一下,如果只有线程1更新counter变量,但是线程1和线程2却可能拿到的不是最新的值。如果没用申明volatile,JVM并不保证counter变量的值马上写回到主内存中,那就意味着CPU cache中的值并不是最新的值。如下图所示:
字节码

这就是线程可见性的问题。线程2并没有实时看到最新的值,因为JVM会先从CPU cache中读取值。

如果申明为volatile,则线程读取值,必须从主内存中读取,写值,必须写到主内存中,这就保证了所有的值对于线程是可见的。但是这不能说明它是线程安全的。这是两个不同的概念。

例子
[java]
package com.learn.core.ch02;

public class VolatileTest {

private int count = 0;

public static void main(String[] args) throws InterruptedException {
while (true) {
VolatileTest test = new VolatileTest();
test.setCount(0);
Thread[] ts = new Thread[2];
for (int i = 0; i < 2; i++) {
ts[i] = new Thread(new MyRun(test, i));
ts[i].start();
}

for (Thread t : ts) {
t.join();
}
Thread.sleep(2);

}
}

public static class MyRun implements Runnable {

private VolatileTest test;
private int index;

public MyRun(VolatileTest test2, int index) {
this.test = test2;
this.index = index;
}

@Override
public void run() {
if (index == 0) {
test.increment();
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (test.getCount() == 0) {
System.out.println(Thread.currentThread().getName() + "—–" + test.getCount());
}
}
}

public int getCount() {
return count;
}

public void increment() {
count++;
}

public void setCount(int count) {
this.count = count;
}

}

[/java]
Thread-10181—–0
Thread-10957—–0
Thread-10959—–0
Thread-11643—–0
Thread-12177—–0
Thread-12269—–0
Thread-12345—–0
Thread-12929—–0
Thread-13221—–0
加上volatile关键字,就可以保证永远拿到最新的值,前提是写的线程先执行。

说说并发编程 ThreadLocal

类ThreadLocal 提供线程局部变量。这些局部变量不同于正常的变量,每个线程都有自己独立初始化的副本,可以通过threadLocal的set/get方法去改变它的。一般ThreadLocal 常用在类变量上。比如DateFormat类不是线程安全的,没有必要所有的方法都加上同步,这是利用ThreadLocal就可以达到效果。

创建ThreadLocal
[java]
private ThreadLocal myThreadLocal = new ThreadLocal();
[/java]

访问ThreadLocal
[java]
//设置变量
myThreadLocal.set("A thread local value");
//读取local变量
String threadLocalValue = (String) myThreadLocal.get();
[/java]

初始化ThreadLocal 变量

[java]
private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
@Override protected String initialValue() {
return "This is the initial value";
}
};
[/java]

例子
[java]
package com.learn.core.ch02;

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadId {

// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);

// Thread local variable containing each thread’s ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {

@Override
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};

// Returns the current thread’s unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}

public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new MyRunnable()).start();
}
}

public static class MyRunnable implements Runnable {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(threadId.get());
}

}
}

}
[/java]

总结:以下2中情况,可以考虑使用

  • 当每个线程仅仅想拥有自己的一些变量,不想所有的线程共享
  • 一些非线程安全的类变量或者工具类,为了保证线程安全,可以通过ThreadLocal作为线程局部变量达到线程安全的效果
  • Excel Pivot table

    最近在重构一个项目,开发已经完成,为了给用户和PM展示数据前后的变化,会用到很多报表工具,今天介绍的pivot table就是一个很好用工具,可以很详细的展示不同维度的数据总和。下面就简单介绍一下:

    Pivot tables are one of Excel’s most powerful features. A pivot table allows you to extract the significance from a large, detailed data set.

    Our data set consists of 214 rows and 6 fields. Order ID, Product, Category, Amount, Date and Country.

    字节码

    Insert a Pivot Table
    To insert a pivot table, execute the following steps.

    1. Click any single cell inside the data set.
    2. On the Insert tab, click PivotTable.

    字节码
    The following dialog box appears. Excel automatically selects the data for you. The default location for a new pivot table is New Worksheet.

    3. Click OK.
    字节码

    Drag fields
    The PivotTable field list appears. To get the total amount exported of each product, drag the following fields to the different areas.

    1. Product Field to the Row Labels area.
    2. Amount Field to the Values area.
    3. Country Field to the Report Filter area.
    字节码

    Below you can find the pivot table. Bananas are our main export product. That’s how easy pivot tables can be!
    字节码
    Sort
    To get Banana at the top of the list, sort the pivot table.

    1. Click any cell inside the Total column.
    2. The PivotTable Tools contextual tab activates. On the Options tab, click the Sort Largest to Smallest button (ZA).

    PivotTable Tools Contextual Tab

    Result

    Filter
    Because we added the Country field to the Report Filter area, we can filter this pivot table by Country. For example, which products do we export the most to France?

    1. Click the filter drop-down and select France.
    Result. Apples are our main export product to France.

    Filtered Pivot Table
    Note: you can use the standard filter (triangle next to Product) to only show the totals of specific products.

    Change Summary Calculation
    By default, Excel summarizes your data by either summing or counting the items. To change the type of calculation that you want to use, execute the following steps.

    1. Click any cell inside the Total column.
    2. Right click and click on Value Field Settings…

    Value Field Settings

    3. Choose the type of calculation you want to use. For example, click Count.

    Summarize Value Field By

    4. Click OK.
    Result. 16 out of the 28 orders to France were ‘Apple’ orders.

    Two-dimensional Pivot Table
    If you drag a field to the Row Labels area and Column Labels area, you can create a two-dimensional pivot table. For example, to get the total amount exported to each country, of each product, drag the following fields to the different areas.

    1. Country Field to the Row Labels area.
    2. Product Field to the Column Labels area.
    3. Amount Field to the Values area.
    4. Category Field to the Report Filter area.

    Create Two-dimensional Pivot Table

    Below you can find the two-dimensional pivot table.

    Two-dimensional Pivot Table in Excel

    To easily compare these numbers, create a pivot chart and apply a filter. Maybe this is one step too far for you at this stage, but it shows you one of the many other powerful pivot table features Excel has to offer.

    Pivot Chart
    字节码

    很好用的一个工具!