volatile关键字

来源:互联网 发布:淘宝网签证 编辑:程序博客网 时间:2024/06/04 18:06

你也一定听说过i++是非线程安全的.在不知道volatile的情况下,通过锁变量的方式来使变量安全,因为变量对于线程之间不可见,但是类似这种悲观锁效率是极低的.

1.volatile使用场景

package com.wpx.thread.demo02;/** * volatile关键字 * 使变量在多个线程之间可见 * @author wangpx */public class demo01 extends Thread{/** * 没有 volatile 关键字    进入run方法isRunning的值已经被设置为了falsefalse线程并没有停止  因为变量虽然发生变化,对于线程所持有的对象没有同步,就像刚开始说的,因为刻板印象有 volatile 关键字进入run方法isRunning的值已经被设置为了false线程停止false线程停止,volatile修饰的变量在修改后会强制同步
 */private volatile  boolean isRunning=true;private void setRunning(boolean isRunning) {this.isRunning=isRunning;}@Overridepublic void run() {System.out.println("进入run方法");while(isRunning == true) {//System.out.println("true");}System.out.println("线程停止");}public static void main(String[] args) throws InterruptedException {demo01 d=new demo01();d.start();Thread.sleep(3000);d.setRunning(false);System.out.println("isRunning的值已经被设置为了false");Thread.sleep(1000);System.out.println(d.isRunning);}}

2.volatile只保证可见性,并不保证一致性

package com.wpx.thread.demo02;import java.util.concurrent.atomic.AtomicInteger;/** * volatile关键字虽然拥有多个线程之间的可见性,但不具备同步性 * 算是一个轻量级的synchronized,性能要比synchronized强的多,不会造成阻塞 * 在netty的底层代码就大量使用volatile * @author wangpx */public class demo02 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);}@Overridepublic void run() {addCount();}public static void main(String[] args) {demo02[] arr=new demo02[10];for (int i=0;i<10;i++) {arr[i]=new demo02();}for(int i=0;i<10 ;i++) {arr[i].start();}}}
3.Atomic类

package com.wpx.thread.demo02;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;/** * 由于volatile关键字只具有可见性,没有原子性 *  * 要实现原子性一般使用atomic类系列对象,支持原子性操作 *  * atomic类只保证本身方法原子性,并不保证多次操作的原子性 * @author wangpx */public class demo03 {private static AtomicInteger count=new AtomicInteger(0);/** * 多个addAndGet在一个方法中是非原子性的,需要加synchronized修饰 * synchronized *  */public   int multiAdd() {try {Thread.sleep(1000);} 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  demo03 d=new demo03();List<Thread> ts=new ArrayList<>();for(int i =100 ;i >0 ;i--) {ts.add(new Thread( () -> System.out.println(d.multiAdd()) ));}for (Thread thread : ts) {thread.start();}}}


线程通信:线程是操作系统中独立的个体,但如果这些个体不经过特殊的处理就不能成为一个整体,线程之间的通信就成了成为整体的方式之一,当线程存在通讯指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员在线程任务在处理的过程中进行有效的把控和监督

使用wait/motify方法实现线程间的通信

wait和notify必须配合synchronized关键字使用

wait方法释放锁,notify方法不释放锁

4.一个经典的面试题

package com.wpx.thread.demo02;/** *  * 通过最原始的方法让线程通信,用一个死循环来不断判断 当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程收到通知 t2i==5线程停止Exception in thread "t2" java.lang.ArithmeticException: / by zeroat com.wpx.thread.demo02.demo04.lambda$1(demo04.java:36)at java.lang.Thread.run(Thread.java:748)当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素 *  */import java.util.ArrayList;import java.util.List;public class demo04 {private  volatile static List list=new ArrayList();public void add() {list.add("wpx");}public int size() {return list.size();}public static void main(String[] args) {final demo04 d=new demo04();Thread t1=new  Thread(() ->  {try {for(int i=0;i< 10;i++ ) {d.add();System.out.println("当前线程 "+Thread.currentThread().getName()+"添加了一个元素");Thread.sleep(1000);}} catch (InterruptedException e) {e.printStackTrace();}},"t1") ;Thread t2=new  Thread(() ->  {while(true) {if(d.size() == 5) {System.out.println("当前线程收到通知 "+Thread.currentThread().getName()+"i==5线程停止"); int i=1/0;}}},"t2") ;t1.start();t2.start();}}
5.通过wait/notify优化

package com.wpx.thread.demo02;/** *  当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素已经发出通知当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程t2收到通知停止..Exception in thread "t2" java.lang.ArithmeticException: / by zeroat com.wpx.thread.demo02.demo05.lambda$1(demo05.java:51)at java.lang.Thread.run(Thread.java:748)线程t2先启动,拿到锁执行到lock.wait()方法会释放锁t1线程拿到锁,当执行到 list.size()== 5 发出通知但lock.notify()方法不会释放锁,所以在整个t1执行完后才会执行t2线程 */import java.util.ArrayList;import java.util.List;public class demo05 {private  volatile static List list=new ArrayList();public void add() {list.add("wpx");}public int size() {return list.size();}public static void main(String[] args) { final demo05 d=new demo05();//实例化出来一个lockfinal Object lock =new Object();Thread t1=new Thread( () -> {try {synchronized (lock) {for(int i=0;i< 10;i++ ) {d.add();System.out.println("当前线程 "+Thread.currentThread().getName()+"添加了一个元素");Thread.sleep(1000);if(d.size() == 5) {lock.notify();System.out.println("已经发出通知");}}}} catch (InterruptedException e) {e.printStackTrace();}},"t1") ;Thread  t2=new Thread(() -> {synchronized (lock) {if(d.size() !=5 ) {try {lock.wait();Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("当前线程"+Thread.currentThread().getName()+"收到通知停止..");int i=1/0;}},"t2");t2.start();t1.start();}}
6.实时通讯

package com.wpx.thread.demo02;/**当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素已经发出通知当前线程 t1添加了一个元素当前线程t2收到通知停止..Exception in thread "t2" 当前线程 t1添加了一个元素java.lang.ArithmeticException: / by zeroat com.wpx.thread.demo02.demo06.lambda$1(demo06.java:63)at java.lang.Thread.run(Thread.java:748)当前线程 t1添加了一个元素当前线程 t1添加了一个元素当前线程 t1添加了一个元素 */import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;public class demo06 {private  volatile static List list=new ArrayList();public void add() {list.add("wpx");}public int size() {return list.size();}public static void main(String[] args) { final demo06 d=new demo06();final CountDownLatch countDownLatch=new CountDownLatch(1);Thread t1=new Thread( () -> {try {for(int i=0;i< 10;i++ ) {d.add();System.out.println("当前线程 "+Thread.currentThread().getName()+"添加了一个元素");Thread.sleep(1000); if(d.size() == 5) {countDownLatch.countDown();System.out.println("已经发出通知");}}} catch (InterruptedException e) {e.printStackTrace();}},"t1") ;Thread  t2=new Thread(() -> {if(d.size() !=5 ) {try {countDownLatch.await();Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("当前线程"+Thread.currentThread().getName()+"收到通知停止..");int i=1/0;},"t2");t2.start();t1.start();}}