博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程基础之synchronized和volatile
阅读量:6268 次
发布时间:2019-06-22

本文共 4351 字,大约阅读时间需要 14 分钟。

多线程安全三大特性:

(1)原子性:

指一系列操作要么一起执行完成,要么一起不执行。例如i++操作其实并不是原子的,线程需要先获取到i的值然后在线程内存中对i的值进行+1再刷新到主内存中,在这个期间可能有别的线程对i的值进行了修改,这样得出的结果就是错误的,所以我们需要同步锁Synchronized(Volatile并不是原子性)。

(2)可见性:

线程之间变量相互可见。假设有一个全局变量i的值为0,同时有线程A和线程B对一个全局变量i执行++操作那么在JMM(JAVA内存模型)中由A线程获取全局变量i的值后在线程内存中对i执行++操作(由于线程互相之间不可见此时对B线程来说i还是0),此时B线程同时进行与A线程一样的操作,在最后刷新到主内存中那么i的值就为1而不是2。这就是线程之间互相不可见造成的线程不安全。

                                                    线程安全情况下

                                                    线程不安全情况下

(3)有序性:

即程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

synchronized:

java中自带的同步锁,可修饰方法或用来同步代码块

public class ThreadDemo implements Runnable {    static int a=100;	Object obj=new Object();	@Override	public void run() {		test3();	}	/**	 * Synchronzed修饰方法 此时相当于使用Synchronized(this){...}包裹了方法中的所有代码	 */	public  synchronized void test1() {		System.out.println();	}		/**	 *  synchronized 代码块选择一个对象作为对象锁 只有获取到该对象锁的线程才可以访问代码块	 */	public void test2() {		synchronized(obj){			System.out.println();		}	}		/**	 *  synchronized 修饰静态方法 相当于synchronized(ThreadDemo.class) 以类字节码文件作为对象锁  所有该类的对象想访问代码都必须获取同一个锁	 */	public synchronized static void test3() {		for (int i = 0; i < 50; i++) {			try {				Thread.sleep(300);			} catch (InterruptedException e) {								e.printStackTrace();			}			a--;			System.out.println(Thread.currentThread().getName()+"----"+a);		}	}} 复制代码

1.修饰方法:

等于synchronized(this){...},一个对象实例中同时只能有一条线程访问该方法,如果该对象实例中有其它修饰了synchronized的方法,那么它们将共用一把锁。

2.同步代码块:

语法:synchronized(obj){...},一个对象实例中同时只能有一条线程访问该同步代码块,如果该对象实例中有其它使用了这个obj对象锁修饰的代码块,那么它们将共用一把锁。

3.修饰静态方法:

等于synchronized(xxx.class){...},所有该class的实例对象共用同一把锁,以class字节码文件作为对象锁。

代码如下:

public class ThreadDemo implements Runnable {    private static int a=0;	@Override	public void run() {		add();	}   	public static void main(String[] args) {		Thread t1=new Thread(new ThreadDemo(),"线程1");		Thread t2=new Thread(new ThreadDemo(),"线程2");		t1.start();		t2.start();	}		public synchronized static void add() {		for (int i = 0; i < 50; i++) {			try {				Thread.sleep(300); /**非线程安全情况下让线程休眠堆积 重新调度 产生竟态条件**/			} catch (InterruptedException e) {								e.printStackTrace();			}			a++;			System.out.println(Thread.currentThread().getName()+":----"+a);		}			}}复制代码

运行结果:

线程1:----1线程1:----2线程1:----3线程1:----4……线程1:----40线程1:----41线程1:----42线程1:----43线程1:----44线程1:----45线程1:----46线程1:----47线程1:----48线程1:----49线程1:----50线程2:----51线程2:----52线程2:----53线程2:----54线程2:----55线程2:----56线程2:----57线程2:----58线程2:----59……线程2:----96线程2:----97线程2:----98线程2:----99线程2:----100复制代码

synchronized在使用时需要注意性能问题,应自己衡量好性能与线程安全之间的平衡。复制代码

volatile:

java自带的关键字,用来修饰变量,被声明的变量对所有线程可见同时禁止重排序,但不保证原子性,假设有多条线程同时对变量值进行修改还是会出现线程不安全。

修改上述代码:

public class ThreadDemo implements Runnable {    private volatile static int a=0;	@Override	public void run() {		add();	}   	public static void main(String[] args) {		Thread t1=new Thread(new ThreadDemo(),"线程1");		Thread t2=new Thread(new ThreadDemo(),"线程2");		t1.start();		t2.start();	}		public  static void add() {		for (int i = 0; i < 50; i++) {			try {				Thread.sleep(300); /**非线程安全情况下让线程休眠堆积 重新调度 产生竟态条件**/			} catch (InterruptedException e) {								e.printStackTrace();			}			a++;			System.out.println(Thread.currentThread().getName()+":----"+a);		}			}}复制代码

运行结果:

线程2:----1线程1:----2线程2:----4线程1:----4线程2:----5线程1:----6线程2:----7线程1:----8线程2:----9线程1:----10线程1:----11线程2:----12线程1:----13线程2:----14线程1:----16线程2:----16线程1:----18线程2:----18线程1:----19线程2:----20线程1:----21线程2:----22线程1:----23线程2:----23线程1:----24线程2:----25线程1:----26线程2:----27线程2:----29线程1:----29线程1:----30线程2:----31线程1:----32线程2:----33线程1:----34线程2:----35线程1:----36线程2:----37线程1:----38线程2:----39线程1:----40线程2:----41线程1:----42线程2:----43线程1:----44线程2:----45线程1:----46线程2:----47线程1:----48线程2:----49线程1:----50线程2:----51线程1:----52线程2:----53线程1:----54线程2:----55线程1:----56线程2:----57线程1:----58线程2:----59线程1:----60线程2:----61线程1:----62线程2:----63线程1:----64线程2:----65线程1:----66线程2:----67线程1:----68线程2:----69线程1:----70线程2:----71线程1:----72线程2:----73线程1:----74线程2:----75线程1:----76线程2:----77线程1:----78线程2:----79线程1:----80线程2:----81线程1:----82线程2:----83线程1:----84线程2:----85线程1:----86线程2:----87线程1:----88线程2:----89线程1:----90线程2:----91线程1:----92线程2:----93线程1:----94线程2:----95线程2:----97线程1:----97线程1:----98线程2:----99复制代码

线程1和线程2两个线程还是会得出相同的值,这就是因为同时有2个线程修改了值。

总结:

volatile只保证线程之间的可见性和禁止重排序,但是不能保证原子性。synchronized可以保证原子性但是synchronized多了会影响性能,jdk1.6后对synchronized有了优化,如何使用需要看实际情况自己衡量。

转载于:https://juejin.im/post/5cb978a951882532525a0ecd

你可能感兴趣的文章
C#.NET常见问题(FAQ)-索引器indexer有什么用
查看>>
hadoop YARN配置参数剖析—MapReduce相关参数
查看>>
Java 正则表达式详细使用
查看>>
【ADO.NET】SqlBulkCopy批量添加DataTable
查看>>
SqlServer--bat批处理执行sql语句1-osql
查看>>
Linux系列教程(十八)——Linux文件系统管理之文件系统常用命令
查看>>
laravel安装初体验
查看>>
用yum查询想安装的软件
查看>>
TIJ -- 吐司BlockingQueue
查看>>
数据库分页查询
查看>>
[编程] C语言枚举类型(Enum)
查看>>
[Javascript] Compose multiple functions for new behavior in JavaScript
查看>>
ASP.NET MVC性能优化(实际项目中)
查看>>
ES6里关于类的拓展(一)
查看>>
零元学Expression Blend 4 - Chapter 46 三分钟快速充电-设定Margin的小撇步
查看>>
Format Conditions按条件显示表格记录
查看>>
RichTextBox指定全部文字显示不同颜色及部分文字高亮颜色显示
查看>>
mysql优化----explain的列分析
查看>>
Python正则表达式
查看>>
Java中CAS详解
查看>>