4000-520-616
欢迎来到免疫在线!(蚂蚁淘生物旗下平台)  请登录 |  免费注册 |  询价篮
主营:原厂直采,平行进口,授权代理(蚂蚁淘为您服务)
咨询热线电话
4000-520-616
当前位置: 首页 > 新闻动态 >
热卖商品
新闻详情
Java 并发编程(四)线程同步关键字volatile和synchroniz..._CSDN博客
来自 : CSDN技术社区 发布时间:2021-03-24

线程 是程序执行流的最小单位。是进程中的一个实体 是被系统独立调用和分派的基本单元 线程自己不拥有系统资源 只是拥有在运行中必不可少的资源 但是可以与同属一个进程的其他线程共享全部资源 一个线程可以创建和撤销另一个线程 同一个进程中的多个线程之间可以兵法执行。单个程序中同时运行多个线程完成不同工作 称之为多线程。

java中并发包 java.util.concurrent(JUC)

##1. volatile关键字
作用

线程可见性 但不保证操作原子性防止指令重排

volatile修饰的变量/对象 每次修改后会cpu会通知其他线程 使得其他线程使用该变量/对象时从主内存中获取。
volatile不保证原子性 示例
例子1:不使用volatile关键字

package com.multithread;import java.util.concurrent.TimeUnit;public class VolatileTest { public static int count  public static void inc() { try { TimeUnit.MILLISECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); count  public static void main(String[] args) { for (int i i 1000; i ) { new Thread(new Runnable() { Override public void run() { VolatileTest.inc(); }).start(); System.out.println( count VolatileTest.count);

运行结果 count 963 与期望值1000不同。

例子2:使用volatile关键字

package com.multithread;import java.util.concurrent.TimeUnit;public class VolatileTest { public volatile static int count  public static void inc() { try { TimeUnit.MILLISECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); count  public static void main(String[] args) { for (int i i 1000; i ) { new Thread(new Runnable() { Override public void run() { VolatileTest.inc(); }).start(); System.out.println( count VolatileTest.count);

运行结果 count 980 与期望值1000不同。

1.1 分析原因

JVM在运行时对内存进行分配 其中一个内存区域是JVM虚拟机栈 每个线程在运行时都会有一个线程栈 TLAB–线程本地分配缓冲 其中保存了线程运行时变量值信息。当线程访问某一个对象的值的时候 首先通过对象引用找到对应堆内存的变量的值 然后把堆内存变量的具体值load到线程内存中 建立一个变量的副本 之后线程就不在和对象在堆内存变量值有任何关系 而是直接修改变量副本的值 在线程退出之前会将修改后副本的值会写到对象的堆内存中的值 这样在堆中对象的变量的值就发生变化了。
下图描述了这种交互

\"线程内存与主内存交互\"

说明

read and load 从主存复制变量到当前工作内存use and assign 执行代码 改变共享变量值store and write 用工作内存数据刷新主存相关内容

其中 use and assign 可以多次出现 但是这一些操作并不是原子性 也就是在read load之后 如果主内存count变量发生修改之后 线程工作内存中的值由于已经加载 不会产生对应的变化 所以计算出来的结果会和预期不一样。

对于volatile修饰的变量 jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的。
例如假如线程1 线程2 在进行read,load 操作中 发现主内存中count的值都是5 那么都会加载这个最新的值 在线程1堆count进行修改之后 会write到主内存中 主内存中的count变量就会变为6 线程2由于已经进行read,load操作 在进行运算之后 也会更新主内存count的变量值为6导致两个线程及时用volatile关键字修改之后 还是会存在并发的情况。

2. 关键字synchronized 2.1 Java语言的关键字 同步锁。

synchronized修饰的对象有以下几种

类 作用范围是synchronized后面括号括起来的部分 作用的对象是类的所有对象 静态方法 作用的范围是整个静态方法 作用的对象是类的所有对象 方法 称为同步方法 作用的范围是整个方法 作用的对象是调用这个方法的对象 代码块 称为同步语句块 其作用的范围是大括号{}括起来的代码 作用的对象是调用这个代码块的对象。

作用

多线程的可见性 保证操作原子性防止指令重排

使用的锁

无锁 – new 出来的对象 没有任何同步操作偏向锁 – 对象上有一把锁 该对象就表现为偏向这个线程来获取锁轻量锁 – 对象上有多个锁竞争 就会升级为轻量锁重量锁 – 对象上有很多个锁竞争 而且是在高并发情况下 就会升级为重量级锁 即向cpu申请锁

锁标识

锁的标识是表现在Java 对象head的Mark Word中 使用后2-3位来标识对象的当前锁种类 2.2 例子 2.2.1 修饰类

用法如下

class ClassName { public void method() { synchronized(ClassName.class) { // todo work

示例

public class SynClass implements Runnable { private static int count; public SynClass(int count) { this.count count; Override public void run() { synchronized (SynClass.class) { try { for (int i i i ) { System.out.println( ThreadName: Thread.currentThread().getName() , count count  TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();

调试代码

 SynClass synClassA new SynClass(0); SynClass synClassB new SynClass(0); new Thread(synClassA).start(); new Thread(synClassB).start();

调试结果

ThreadName:Thread-0, count 0ThreadName:Thread-0, count 1ThreadName:Thread-0, count 2ThreadName:Thread-0, count 3ThreadName:Thread-0, count 4ThreadName:Thread-1, count 5ThreadName:Thread-1, count 6ThreadName:Thread-1, count 7ThreadName:Thread-1, count 8ThreadName:Thread-1, count 9

说明 synchronized作用于一个类T时 是给这个类T加锁 T的所有对象用的是同一把锁。

2.2.2 修饰静态方法

用法如下

public static synchronized void method() { // todo work

示例

public class SynStaticMethod implements Runnable{ private static int count; public SynStaticMethod() { count  public static synchronized void method() { try { for (int i i i ) { System.out.println( ThreadName: Thread.currentThread().getName() , count count  TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); Override public synchronized void run() { method();

调试代码

 SynStaticMethod synStaticMethodA new SynStaticMethod(); SynStaticMethod synStaticMethodB new SynStaticMethod(); new Thread(synStaticMethodA).start(); new Thread(synStaticMethodB).start();

调试结果

ThreadName:Thread-0, count 0ThreadName:Thread-0, count 1ThreadName:Thread-0, count 2ThreadName:Thread-0, count 3ThreadName:Thread-0, count 4ThreadName:Thread-1, count 5ThreadName:Thread-1, count 6ThreadName:Thread-1, count 7ThreadName:Thread-1, count 8ThreadName:Thread-1, count 9

说明 静态方法是属于类的而不属于对象的。所以synchronized修饰的静态方法锁定的是这个类的所有对象 与类锁是一样的。

2.2.3 修饰非静态方法

synchronized修饰一个方法很简单 就是在方法的前面加synchronized synchronized修饰方法和修饰一个代码块类似 只是作用范围不一样 修饰代码块是大括号括起来的范围 而修饰方法范围是整个方法。
用法如下

public synchronized void method() { // todo work

示例

定义一个类用来同步方法

public class NotStaticMethodService { private static int count; public NotStaticMethodService() { this.count  public synchronized void synMethod() { for (int i i i ) { try { System.out.println( ThreadName: Thread.currentThread().getName() , count count  TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();

定义线程类

public class SynNotStaticMethod implements Runnable { private NotStaticMethodService notStaticMethodService; public SynNotStaticMethod(NotStaticMethodService notStaticMethodService) { this.notStaticMethodService notStaticMethodService; Override public void run() { notStaticMethodService.synMethod();

调试代码

 NotStaticMethodService notStaticMethodService new NotStaticMethodService(); SynNotStaticMethod synNotStaticMethodA new SynNotStaticMethod(notStaticMethodService); SynNotStaticMethod synNotStaticMethodB new SynNotStaticMethod(notStaticMethodService); new Thread(synNotStaticMethodA).start(); new Thread(synNotStaticMethodB).start();

调试结果

ThreadName:Thread-0, count 0ThreadName:Thread-0, count 1ThreadName:Thread-0, count 2ThreadName:Thread-0, count 3ThreadName:Thread-0, count 4ThreadName:Thread-1, count 5ThreadName:Thread-1, count 6ThreadName:Thread-1, count 7ThreadName:Thread-1, count 8ThreadName:Thread-1, count 9
2.2.4 代码块
 一个线程访问一个对象中的synchronized(this)同步代码块时 其他试图访问该对象的线程将被阻塞。

示例

public class SynBlockRunnable implements Runnable { private static int count; public SynBlockRunnable() { count  Override public void run() { synchronized (this) { for (int i i i ) { try { System.out.println( ThreadName: Thread.currentThread().getName() , count count  TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();

调试代码

 SynBlockRunnable synBlockRunnable new SynBlockRunnable(); new Thread(synBlockRunnable).start(); new Thread(synBlockRunnable).start();

调试结果

ThreadName:Thread-0, count 0ThreadName:Thread-0, count 1ThreadName:Thread-0, count 2ThreadName:Thread-0, count 3ThreadName:Thread-0, count 4ThreadName:Thread-1, count 5ThreadName:Thread-1, count 6ThreadName:Thread-1, count 7ThreadName:Thread-1, count 8ThreadName:Thread-1, count 9

说明

当两个并发线程(thread1和thread2)访问同一个对象(SynBlockRunnable)中的synchronized代码块时 同一时刻只能有一个线程得到执行 另一个线程则受阻塞 必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的 因为在执行synchronized代码块时会锁定当前的对象 只有执行完该代码块才能释放该对象锁 下一个线程才能执行并锁定该对象。

将上面调试代码修改如下

 SynBlockRunnable synBlockRunnableA new SynBlockRunnable(); SynBlockRunnable synBlockRunnableB new SynBlockRunnable(); new Thread(synBlockRunnableA).start(); new Thread(synBlockRunnableB).start();

此时调试结果为

ThreadName:Thread-0, count 0ThreadName:Thread-1, count 1ThreadName:Thread-0, count 2ThreadName:Thread-1, count 2ThreadName:Thread-0, count 3ThreadName:Thread-1, count 4ThreadName:Thread-1, count 5ThreadName:Thread-0, count 6ThreadName:Thread-1, count 7ThreadName:Thread-0, count 8

说明

这时创建了两个SynBlockRunnable的对象synBlockRunnableA和synBlockRunnableB 线程Thread1执行的是synBlockRunnableA对象中的synchronized代码(run) 而线程Thread2执行的是synBlockRunnableB对象中的synchronized代码(run) 但synchronized锁定的是对象 这时会有两把锁分别锁定synBlockRunnableA对象和synBlockRunnableB对象 这两把锁是互不干扰的 不形成互斥 所以两个线程可以同时执行。

2.3 字节码实现

可通过javap -c命令来生成类的字节码中包含 monitorenter和monitorexit指令。synchronized关键字是基于这两个指令来实现锁的获取和释放过程。

2.4 Synchronized 的实现过程 Java代码 synchronized 关键字字节码 monitorenter monitorexit锁升级 执行过程中自动升级操作系统 cpu汇编指令 lock comxchg

本文链接: http://synblock.immuno-online.com/view-711951.html

发布于 : 2021-03-24 阅读(0)
公司介绍
品牌分类
联络我们
服务热线:4000-520-616
(限工作日9:00-18:00)
QQ :1570468124
手机:18915418616
官网:http://