Synchronized关键字
静态的synchronized方法以class对象作为锁,又称Intrinsic Lock或Monitor锁;
Synchronized (lock) {
//访问或修改由锁保护的共享状态
}
同步代码块:对象的Monitor锁底层有monitorenter和monitorexit指令,monitorenter指令将计数器值加1,monitorexit将计数器值减1;当计数器值为0时monitor锁才能由线程获取,且计数器值只能由持有Monitor锁的线程修改。并且,无论方法正常结束还是异常终止,monitorexit指令都会执行,以确保线程释放monitor锁。
同步方法:使用的是运行时常量池中方法的 ACC_SYNCHRONIZED 标志,原理与以上类似。
示例代码:
public class CountSync implements Runnable {
private int i = 0;
public synchronized void increase(){
i++;
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
increase();
}
}
public synchronized int getI() {
return i;
}
public static void main(String[] args) throws InterruptedException {
CountSync sync = new CountSync();
Thread t1 = new Thread(sync);
Thread t2 = new Thread(sync);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(sync.getI());
}
}
注:synchronized关键字用于静态方法时使用的是类对象的锁
Volatile变量
变量的更新操作会被及时通知到其他线程,只确保可见性不保证原子性,但volatile 关键宇能够保障对 long/double 型变量的写操作具有原子性。
加入volatile会禁止指令重排,强制对缓存的修改操作立即写入主内存;其他线程的读操作直接从主内存读取最新值。
使用场景:1. 对变量的写操作不依赖当前值;
2. 该变量没有包含在具有其他变量的不变式中。
示例代码:
public class VolaThread implements Runnable {
public static volatile int n = 0;
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
increment();
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void increment() {
n++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new VolaThread());
Thread t2 = new Thread(new VolaThread());
Thread t3 = new Thread(new VolaThread());
Thread t4 = new Thread(new VolaThread());
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("n is :" + n);
}
}
ThreadLocal类
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。使线程中的某个值与保存值的对象关联起来,ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享
ThreadLocal使用ThreadLocalMap内部静态类,实际调用ThreadLocalMap的set和get方法设置和获取值,key为当前ThreadLocal对象,value为变量副本。
示例代码:
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThreadLocal {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss SSS");
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
@Override
protected Object initialValue() {
System.out.println("[" + DATE_FORMAT.format(new Date()) +"] " + Thread.currentThread().getName() + " invokes method initialValue, return Default value");
return null;
}
};
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyIntegerTask("IntegerTask1"));
Thread t2 = new Thread(new MyStringTask("StringTask1"));
Thread t3 = new Thread(new MyIntegerTask("IntegerTask2"));
Thread t4 = new Thread(new MyStringTask("StringTask2"));
t1.start();
t2.start();
t3.start();
t4.start();
//等待t1,t2,t3,t4死掉
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("******************All done*****************");
System.out.println("线程" + Thread.currentThread().getName() + " get variable, result " + MyThreadLocal.threadLocal.get());
MyThreadLocal.threadLocal.remove();
}
public static class MyIntegerTask implements Runnable {
private String name;
MyIntegerTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (null == MyThreadLocal.threadLocal.get()) {
MyThreadLocal.threadLocal.set(0);
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ": 0");
} else {
int num = (int) MyThreadLocal.threadLocal.get(); //没有抛ClassCastException
MyThreadLocal.threadLocal.set(num + 1);
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":" + MyThreadLocal.threadLocal.get());
if (i == 3) {
MyThreadLocal.threadLocal.remove();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static class MyStringTask implements Runnable {
private String name;
MyStringTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (null == MyThreadLocal.threadLocal.get()) {
MyThreadLocal.threadLocal.set("a");
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":a");
} else {
String string = (String) MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(string + "a");
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":" + MyThreadLocal.threadLocal.get());
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
注:1. 使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2. JDK建议ThreadLocal定义为private static,这样ThreadLocal弱引用的问题则不存在了
Final域
用于构造不可变对象,能确保初始化过程的安全性
示例代码:略
参考:《Java并发编程实战》、深入理解Java并发之synchronized实现原理、正确使用 Volatile 变量、ThreadLocal用法详解和原理