多线程的常用关键字及示例代码(synchronizedvolatile)
Posted 零
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程的常用关键字及示例代码(synchronizedvolatile)相关的知识,希望对你有一定的参考价值。
1.synchronized
synchornized可以修饰方法跟代码块,核心本质是锁对象,如果是static修饰的,也可以锁类变量
注意:理解Java对象在内存中的组成:对象头,实际数据,对齐填充,重点理解对象头中的 markword
线程拥有锁,锁的是对象,对象的头信息中,指向的指针是线程
偏向锁,自旋锁,轻量锁,重量锁
1.偏向锁:当程序没有竞争,取消同步操作,发生竞争时,升级为轻量锁
2.轻量锁:(对象的头信息markword中存放了锁的状态和线程持有的索)如果当前线程用cas将markword锁记录(Lock Record)指针替换成功,获取轻量锁,如果失败尝试自旋,成功获取轻量锁,失败膨胀为重量锁
3.自旋锁:默认10次,JDK1.8变成自适应自旋,会根据上一个线程自旋的时间的状态来自动增减对该锁的自旋次数,(相当于循环获取锁)
4.重量锁:竞争失败后,阻塞
修饰方法:
- 通过ACC_SYNCHORDIZED标记来实现同步,方法级的同步时隐式的
- 同步方法在常量池中会有一个ACC_SYNCHORDIZED的标识,当线程进入某个方法的时候,会判断有没有这个标识,如果有会获得一个监视器锁(monitorenter),运行结束释放锁,有其他线程在这个过程中访问该方法会被阻塞,方法出异常时,这个锁会被释放
修饰代码块:
- 每个对象都维护着一个被锁次数的计数器,未被锁的对象计数器为0,当线程获取锁(执行monitorenter后),计数器自增1,当同一线程再次获取该对象的锁,计数器继续自增,当同一对象释放锁(执行monitorexit)的时候,计数器自减1,当计数器为0的时候,其他线程可以获取锁
示例代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDemo04 implements Runnable
private int num = 1000;
private int count = 0;
@Override
public void run()
Thread thread = Thread.currentThread();
while (num > 0)
synchronized(this)
if (num == 0)
return;
count++;
num--;
System.out.println(thread.getName() + "抢到了第" + count + "张票" + ",剩余" + num + "张票");
public static void main(String[] args) throws InterruptedException
ThreadDemo04 threadDemo04 = new ThreadDemo04();
Thread thread1 = new Thread(threadDemo04,"张三");
Thread thread2 = new Thread(threadDemo04,"李四");
Thread thread3 = new Thread(threadDemo04,"王五");
thread1.start();
thread2.start();
thread3.start();
2.volatile
volatile关键字的作用是:不能保证原子性,但可以保证内存可见性,基本的核心思想就是,当我们修改完成后,将值强制写入主内存,使变量在多个线程间可见(具有可见性)
示例代码
public class VolatileThread extends Thread
boolean flag = false;
int num = 0;
@Override
public void run()
while (!flag)
num++;
public static void main(String[] args) throws InterruptedException
VolatileThread volatileThread = new VolatileThread();
volatileThread.start();
Thread.sleep(1000);
volatileThread.flag = true;
while (true)
System.out.println(volatileThread.num);
Thread.sleep(1000);
可以看到,在此代码中,每隔一秒打印一次i的值,发现i值不断在增长,主线程中将vt.flag设置为true,但是没有影响到子线程中的flag。我们把flag变量加上volatile修饰后,i值将不再增长,证明子线程每次读取flag值时间会从主内存区同步。
如果我们不加上volatile修饰,而是的子线程循环中加入一条打印语句,可以发现运行一段时间后,子线程的flag值也一样被同步了。
**JVM会尽力保证内存的可见性,即便这个变量没有加同步关键字。**换句话说,只要CPU有时间,JVM会尽力去保证变量值的更新。这种与volatile关键字的不同在于,volatile关键字会强制的保证线程的可见性。而不加这个关键字,JVM也会尽力去保证可见性,但是如果CPU一直有其他的事情在处理,它也没办法。最开始的代码,一直处于死循环中,CPU处于一直被饱受占用的时候,这个时候CPU没有时间,JVM也不能强制要求CPU分点时间去取最新的变量值。而加了System.out.println之后,由于屏幕输出其实是比较耗时的,这个时候CPU就有可能有时间去保证内存的可见性
以上是关于多线程的常用关键字及示例代码(synchronizedvolatile)的主要内容,如果未能解决你的问题,请参考以下文章