1.volatile关键字 线程变量共享
/**
* @author
* @date 2018/10/30 20:36
*/
public class RunThread extends Thread{
private volatile boolean isRunning =true;
private void setRunning(boolean isRunning){
this.isRunning=isRunning;
}
public void run(){
System.out.println("进入run方法");
while (isRunning==true){
// System.out.println("---------");
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(3000);
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
Thread.sleep(1000);
System.out.println(rt.isRunning);
}
}
用volatile修饰的时候,则当变量改变的时候强制执行引擎去主内存里读取
如果不用volatile修饰, java会为每个线程开辟单独的空间,设置了isrunning后,会无效。 线程不会停止
发现了一个奇怪的问题:
当里面放了一个输出语句的时候,线程会停止。
package com.wq.thread01;
import com.sun.org.apache.xpath.internal.WhitespaceStrippingElementMatcher;
/**
* @author
* @date 2018/12/13 18:21
*/
public class RunThread extends Thread{
/**
*isRunning 加了volatile关键字就会保证isRunning在线程之间的可见性 改了立马可见
*/
private volatile boolean isRunning=true;
public void setRunning(boolean isRunning){
this.isRunning=isRunning;
}
@Override
public void run() {
System.out.println("进入run方法");
while (isRunning==true){
/*for (int i=0;i<=10000;i++){
int a=i;
}*/
System.out.print("");
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(3000);
rt.setRunning(false);
System.out.println("isrunng的值已经设置了false");
}
}
这是为什么 百度了一下。 不知道正确性:
如上面所示,加了System.out.println之后,线程能停止了。有的人会说,println的源码里面有synchronized关键字,所以会同步变量stopRequested的值。这种是很不正确的理解,同步关键字同步的是同步块里面的变量,stop在这个同步代码块之外。
真正的原因是这样的:JVM会尽力保证内存的可见性,即便这个变量没有加同步关键字。换句话说,只要CPU有时间,JVM会尽力去保证变量值的更新。这种与volatile关键字的不同在于,volatile关键字会强制的保证线程的可见性。而不加这个关键字,JVM也会尽力去保证可见性,但是如果CPU一直有其他的事情在处理,它也没办法。最开始的代码,一直处于试了循环中,CPU处于一直被饱受占用的时候,这个时候CPU没有时间,JVM也不能强制要求CPU分点时间去取最新的变量值。而加了System.out.println之后,由于内部代码的同步关键字的存在,导致CPU的输出其实是比较耗时的。这个时候CPU就有可能有时间去保证内存的可见性,于是while循环可以被终止。
其实,也可以在while循环里面加上sleep,让run方法放弃cpu,但是不放弃锁,这个时候由于CPU有空闲的时候就去按照JVM的要求去保证内存的可见性。如下图所示。 run方法里面休息了3秒,cpu有充足的空闲时间去取变量的最新值,所以循环执行一次就停止了。
1.volatile有可见性 但没有原子性
package com.wq.thread01;
import java.util.concurrent.atomic.AtomicInteger;
/**volatile有可见性 但没有原子性
*
*
* @author
* @date 2018/12/13 19:14
*/
public class VolatileNoAtomic extends Thread {
private static volatile int count;
//AtomicInteger有原子性
// private static AtomicInteger count =new AtomicInteger(0);
private static void addCount(){
//多个线程调用count++,没有原子性 会出现问题 用atomicInteger
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[10];
for (int i = 0; i <10 ; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
结果:
最后一个不是10000,
package com.wq.thread01;
import java.util.concurrent.atomic.AtomicInteger;
/**volatile有可见性 但没有原子性
*
*
* @author wangqiang
* @date 2018/12/13 19:14
*/
public class VolatileNoAtomic extends Thread {
//private static volatile int count;
//AtomicInteger有原子性
private static AtomicInteger count =new AtomicInteger(0);
private static void addCount(){
//多个线程调用count++,没有原子性 会出现问题 用atomicInteger
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[10];
for (int i = 0; i <10 ; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
用了AtomicInteger输出结果最后一个是10000。
多个线程调用count++,没有原子性 会出现问题 用atomicInteger
3.多个addandget在一个方法内是 非 原子性的,需要加synchronized进行装饰,保证集体原子性
package com.wq.thread01;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author wangqiang
* @date 2018/12/13 19:39
*/
public class AtomicUse {
private static AtomicInteger count = new AtomicInteger(0);
//多个addandget在一个方法内是 非 原子性的,需要加synchronized进行装饰,保证4个集体原子性
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);
return count.get();
}
public static void main(String[] args) {
final AtomicUse au = new AtomicUse();
List<Thread> ts = new ArrayList<>();
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();
}
}
}
注意以上三点