进行多线程编程的时候,需要考虑的是线程间的同步问题。对于共享的资源,需要进行互斥的访问。在Java中可以使用一些手段来达到线程同步的目的:

1. synchronized

2. ThreadLocal,线程本地变量

3. Java.util.concurrent.Lock

Java中,线程会共享堆上的实例变量以及方法区的类变量,而栈上的数据是私有的,不必进行保护。synchronized方法或synchronized块将标记一块监视区域,线程在进入该区域时,需要获得对象锁或类锁,JVM将自动上锁。synchronized提供了两种主要特性:

1. 互斥。互斥是指一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的并发访问,保证一次只有一个线程能够使用该共享数据。

2.可见性。确保释放锁之前对共享数据做出的更改对随后获得该锁的另一个线程是可见的。如果不能保证可见性,也就无法保证数据正确性,这将引发严重问题。volitail关键字同样保证了这种可见性。

在这里,我们将探讨synchronized使用时的三种情况:

1. 在对象上使用synchronized

2. 在普通成员方法上使用synchronized

3. 在静态成员方法上使用synchronized

这三种线程同步的表现有何不同?

下面通过三段示例代码来演示这三种情况。这里模拟线程报数的场景。

情况一:在普通成员函数上使用synchronized

public class MyThread extends Thread {

    public static void main(String[] args) throws Exception {
for (int i = 1; i < 100; i++) {
MyThread t = new MyThread();
t.setName("Thread="+i);
t.start();
Thread.sleep(100);
}
} @Override
public synchronized void run() {
for (int i = 1; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}

对一个成员函数使用synchronized进行加锁,所获取的锁,是方法所在对象本身的对象锁。在这里,每个线程都以自身的对象作为对象锁,要对线程进行同步,要求锁对象必须唯一,所以这里多个线程间同步失败。

情况二:在对象上使用synchronized

这里在类中增加一个成员变量lock,在该变量上使用synchronized:

public class MyThread1 extends Thread {

    private String lock;

    public MyThread1(String lock) {
this.lock = lock;
} public static void main(String[] args) throws Exception {
String lock = new String("lock");
for (int i = 1; i < 100; i++) {
Thread t = new MyThread1(lock);
t.setName("Thread=" + i);
t.start();
Thread.sleep(100);
}
} @Override
public void run() {
synchronized (lock) {
for (int i = 1; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}

100个线程在创建的时候,都传递了同一个lock对象(在main中创建的)去初始化线程类成员lock,因此,这100个线程都在同一个lock对象上进行synchronized同步。因此线程同步成功。

情况三:在静态成员函数上使用synchronized

public class MyThread2 extends Thread {

    public static void main(String[] args) throws Exception {

        for (int i = 1; i < 10; i++) {
Thread t = new MyThread2();
t.setName("Thread=" + i);
t.start();
Thread.sleep(10);
}
} public static synchronized void func() {
for (int i = 1; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
} @Override
public void run() {
func();
}
}

这种情况下,线程获得的锁是对象锁,而对象锁是唯一的,因此多个进程间也能同步成功。

补充:

1. 慎用字符串常量做同步对象,因为JVM内部会把常量字符串转换成同一个对象,同理的,基本数据除了Float和Double外,也有缓存对象[-128,127].

2. synchronized方法继承问题:1. 子类会继承父类的synchronized方法。2. 如果子类重写了父类的synchronized方法,必须也加上synchronized关键字,否则子类中的方法将变成非同步的。 3. 同一个子类对象中,子类的synchronized方法父类的synchronized方法使用的是同一个临界区。

(完)

  

Java锁机制(一)synchronized的更多相关文章

  1. java锁机制

    2.4 锁机制        临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作.这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临 ...

  2. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

  3. Java 锁机制总结

    锁的种类 独享锁 VS 共享锁 独享锁:锁只能被一个线程持有(synchronized) 共享锁:锁可以被多个程序所持有(读写锁) 乐观锁 VS 悲观锁 乐观锁:每次去拿数据的时候都乐观地认为别人不会 ...

  4. [转帖]B4. Concurrent JVM 锁机制(synchronized)

    B4. Concurrent JVM 锁机制(synchronized) https://www.cnblogs.com/zlxyt/p/11050346.html 挺好的 感觉这个文章写的 不过想要 ...

  5. Java锁机制深入理解

    Java锁机制 背景知识 指令流水线 ​ CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. ​ 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...

  6. java锁机制的面试题

    java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...

  7. java 锁机制(synchronized 与 Lock)

    在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...

  8. Java 锁机制 synchronized

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/75126630 本文出自[赵彦军的博客] 1.前言 在多线程并发编程中Synchro ...

  9. JVM锁机制之synchronized

    概述: synchronized是java用于处理多线程同步的一个关键字,用于标记一个方法/代码块,使之成为同步方法/同步块. 用synchronized可以避免多线程处理时的竞态条件问题. 相关概念 ...

  10. Java锁机制了解一下

    前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchro ...

随机推荐

  1. c++语言的设计和演化---在线函数

    开始的c++语言中引入inline函数的目的是处理一些实时的情况,而普通的函数调用的开销无法被接受. 起初是在类的声明中定义inline函数,也只支持成员函数,后来才支持非成员函数:

  2. ES6 迭代器和生成器

    设计为了更高效的数据处理,避免过多for循环嵌套(代码复杂度,跟踪多个循环变量) 1. 迭代器: 为迭代过程设计的接口 所有的迭代器对象都有next()方法,每次调用都返回一个结果对象,对象有两个属性 ...

  3. JS 之 script标签

    1.script标签 1.js代码的解析(包括下载js文件)会阻塞页面加载 2.当js文件放在头部,页面必须等所有js代码都被下载,解析和执行完成后才开始呈现页面内容(遇到body标签才呈现),对于那 ...

  4. redis:order set有序集合类型的操作(有序集合)

    1. order set有序集合类型的操作(有序集合) 有序集合是在无序集合的基础上加了一个排序的依据,这个排序依据叫score,因此声明一个集合为有序集合的时候要加上score(作为排序的依据) 1 ...

  5. 移动端适配问题px->rem方法

    移动端web页面适配问题 1.引入插件 github地址:https://github.com/re54k/mobileweb-utilities/blob/master/util/mobile-ut ...

  6. JAVA自学笔记27

    JAVA自学笔记27 1.类的加载 1)当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. ①加载:就是指将class文件读入内存,并为之创 ...

  7. Hibernate(8)_单向n对n

    1.n-n 的关联必须使用连接表 与1-n映射类似,必须为set集合元素添加 key 子元素,需要指定中间表 2.实体类 Category.java public class Category { p ...

  8. seq_file学习(2)—— seq_file

    li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-le ...

  9. springMVC4(7)模型视图方法源代码综合分析

    在完整web开发中.springMVC主要充当了控制层的角色.它接受视图层的请求.获取视图层请求数据,再对数据进行业务逻辑处理.然后封装成视图层须要的模型数据,再将数据导向到jsp等视图界面. 在前面 ...

  10. 【PHP】解析PHP中的函数

    目录结构: contents structure [-] 可变参数的函数 变量函数 回调函数 自定义函数库 闭包(Closure)函数的使用 在这篇文章中,笔者将会讲解如何使用PHP中的函数,PHP是 ...