接着上一篇文章,下面看看几个比较好理解的知识点!!
volatile
java关键字volatile修饰的变量从字面意义上理解易变的,不稳定的,事实上时告诉编译器该变量是易变的不要对该变量使用缓存等级的优化,每次都从内存地址中读取值。
不过并没有说明在对volatile修饰的变量进行修改后立即写会内存地址,也就是说volatile只提供内存的可见性,而没有提供原子性,所以在高并发下是不安全的。
volatile使用的场景:最好是那种只有一个线程修改变量,多个线程读取变量的地方,对内存可见性高原子性低的地方。
volatile与枷锁机制的区别:枷锁机制确保了可见性和原子性,而volatile只有可见性。
举一个例子看看:
/**
* @classDesc: volatile特性的演示
* @author: hj
* @date:2018年12月12日 下午1:54:12
*/
public class RunThread extends Thread {
private volatile boolean isrunning = true;
// private boolean isrunning = true;
public void setRunning(boolean running) {
this.isrunning = running;
}
@Override
public void run() {
System.out.println("进入run方法..");
int i = 0;
while (isrunning == true) {
// ..
}
System.out.println("线程停止");
}
public static void main(String[] args) {
RunThread rt = new RunThread();
rt.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
}
}
volatile关键字不具备synchronized关键字的原子性(同步)
/**
* volatile关键字不具备synchronized关键字的原子性(同步)
*
* @author hj
*
*/
public class VolatileNoAtomic extends Thread {
// private static volatile int count;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount() {
for (int i = 0; i < 1000; i++) {
// count++ ;
count.incrementAndGet();
}
System.out.println(count);
}
public void run() {
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
原子操作
atomic是不会阻塞线程的,基本类型有atomicinteger atomiclong atomicboolean.。
只要记住一点:多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
/**
* @classDesc: Atomic原子操作的演示
* @author: hj
* @date:2018年12月12日 下午2:20:30
*/
public class AtomicUse {
private static AtomicInteger count = new AtomicInteger(0);
// 多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
/** synchronized */
public synchronized int multiAdd() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4); // +10
return count.get();
}
public static void main(String[] args) {
final AtomicUse au = new AtomicUse();
List<Thread> ts = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
ts.add(new Thread(new Runnable() {
@Override
public void run() {
System.out.println(au.multiAdd());
}
}));
}
for (Thread t : ts) {
t.start();
}
}
}
threadlocal
threadlocal当前线程的副本,在使用Threadlocal维护变量时Threadlocal为该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不改变其他线程的副本。在资源的连接池中使用的比较广泛。
/**
* @classDesc: 数据与线程绑定ThreadLocal使用
* @author: hj
* @date:2018年12月12日 下午2:29:13
*/
public class ConnThreadLocal {
public static ThreadLocal<String> th = new ThreadLocal<String>();
public void setTh(String value) {
th.set(value);
}
public void getTh() {
System.out.println(Thread.currentThread().getName() + ":" + this.th.get());
}
public static void main(String[] args) throws InterruptedException {
final ConnThreadLocal ct = new ConnThreadLocal();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
ct.setTh("张三");
ct.getTh();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
ct.getTh();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2");
t1.start();
t2.start();
}
}
ps:jdk底层实现threadlocal是通过map实现的,所以使用时注意线程的数量,不要超过了限制引起内存溢出。
多线程下的单例模式
单例模式本应该是在设计模式中讲的,也是设计模式中最简单的一种模式。在高并发下传统柜的设计模式就不行了,在这提供2中高并发设计下的单例模式。双检测和类中类(double check ;class in class)
/**
* @classDesc: 高并发下的单例模式,类中类
* @author: hj
* @date:2018年12月12日 下午2:41:41
*/
public class Singletion {
private static class InnerSingletion {
private static Singletion single = new Singletion();
}
public static Singletion getInstance() {
return InnerSingletion.single;
}
}
/**
* @classDesc: 高并发下的单例模式,双检测
* @author: hj
* @date:2018年12月12日 下午2:41:41
*/
public class DubbleSingleton {
private static DubbleSingleton ds;
public static DubbleSingleton getDs() {
if (ds == null) {
try {
// 模拟初始化对象的准备时间...
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DubbleSingleton.class) {
if (ds == null) {
ds = new DubbleSingleton();
}
}
}
return ds;
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}
ps:在纽扣软件的笔试中考了,结果忘了这次就记住,如果在笔试中考到单例模式记得把这个写上,能增色不少。。
面试题
1.原子操作的一定是线程安全的吗??举举例子说明
2.threadlocal使用场景有哪些??使用时注意哪些??
3.volatile是线程安全的吗?使用场景有哪些??对比加锁机制回答。
4.写出单例模式代码,说明在高并发下是安全的吗?如何改进?