- volatile关键字有什么作用?
在JVM1.2之前,Java总是从主存读取变量,但随着JVM的优化,线程可以把主存变量保存在寄存器(工作内存)中操作,线程结束再与主存变量进行同步,然而,当线程没有执行结束就发生了互换这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中变量值的副本,造成数据的不一致。要解决这个问题,就需要把该变量声明为volatile(不稳定的),它指示JVM这个变量是不稳定的,每次使用它都到主存中进行读取,因此多线程环境下volatile关键字的使用变得非常重要。一般说来,多线程环境下各线程间共享的变量都应该加volatile修饰。
public class Test {
private static volatile int count=0;
public static void main(String[] args){
Thread[] threads=new Thread[3];
for(int i=0;i<3;i++){
threads[i]=new Thread(()->{
try{
for(int j=0;j<10;j++){
System.out.println((++count));
Thread.sleep(500);
}
}catch (Exception e){
e.printStackTrace();
}
});
threads[i].start();
}
}
}
当一个线程修改了被其修饰的变量的值,volatile保证了新值能立即同步到主内存,同时其他线程每次使用前都会从主内存读取,从而新值对于其他线程来说是可以立即得知的。
为了尽可能的减小内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则将程序编写顺序打乱,就可能干扰程序的并发执行。添加volatile修饰之后可以阻止指令的重排序,从而实现避免此类情况的发生。
- 编写Java程序模拟烧水泡茶最优工序
import java.util.Date; class HeatUpWater implements Runnable{ static Date date=new Date(); public void run(){ int time=10; while(time!=0){ System.out.println(date+"烧水中"); time--; try{ Thread.sleep(1000); }catch(Exception e){ System.out.println("Error!"); } } } } class WashCup implements Runnable{ static Date date=new Date(); public void run(){ int time=5; while(time!=0){ System.out.println(date+"洗茶杯中"); time--; try{ Thread.sleep(1000); }catch(Exception e){ } } } } class Tea implements Runnable{ static Date date=new Date(); public void run(){ int time=5; while(time!=0){ System.out.println(date+"泡茶中"); time--; try{ Thread.sleep(1000); }catch(Exception e){ } } } } public class Test implements Runnable{ static Date date=new Date(); public static void main(String[] args) throws Exception{ HeatUpWater h1=new HeatUpWater(); WashCup w1=new WashCup(); Tea m1=new Tea(); Thread t1=new Thread(h1); Thread t2=new Thread(w1); Thread t3=new Thread(m1); try { t1.start(); t2.start(); t2.join(); t1.join(); }catch (Exception e){ System.out.println("Error!"); } t3.start(); } public void run(){ System.out.println(date+"泡茶中"); try{ Thread.sleep(1000); }catch (Exception e){ } } }
- 编写一个基于多线程的生产者/消费者Java应用,各产生10个生产者和消费者线程,共享一个缓冲区队列(长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。
class Account{ volatile private int value; public void put(int i){ try{ Thread.sleep(300); }catch(Exception e){} value = value +i; System.out.println(Thread.currentThread().getName()+" 生产者--存入了 "); } synchronized int get(int i){ try{ Thread.sleep(300); }catch(Exception e){} if(value>i){ value = value - i; }else{ i = value; value = 0; } System.out.println(Thread.currentThread().getName()+ " 消费者--消费了 "+i+" 余量为:"+value); return i; } } class Save implements Runnable{ private Account a1; public Save(Account a1){this.a1=a1;} public void run(){ while(true){ a1.put(1); } } } class Fetch implements Runnable{ private Account a1; public Fetch(Account a1){this.a1 = a1 ;} public void run(){ while(true){ a1.put(1); } } } public class Test{ public static void main(String[] args){ Account a1 = new Account(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start(); //10个生产者和消费者 new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); new Thread(new Fetch(a1)).start(); } }
- 阅读公众号“码农翻身”的文章——《我是一个线程》
-
参考地址:https://mp.weixin.qq.com/s/-BMCUuIWYE3O_oC3ZNNJRg