博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java锁机制(一)synchronized
阅读量:5322 次
发布时间:2019-06-14

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

 

进行多线程编程的时候,需要考虑的是线程间的同步问题。对于共享的资源,需要进行互斥的访问。在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方法使用的是同一个临界区。 

(完)

  

转载于:https://www.cnblogs.com/QG-whz/p/8342276.html

你可能感兴趣的文章
Android中ListView的用法及优化
查看>>
JavaScript基本包装类介绍
查看>>
Objective-C设计模式——工厂方法模式virtual constructor(对象创建)
查看>>
glibc-commons 依赖解析 版本错误,xxx is duplicate yyy
查看>>
html5 简单五子棋
查看>>
函数调用的区别:_cdecl以及_stdcall
查看>>
smartctl工具学习网站
查看>>
hdu 3732
查看>>
Persistent services in Android[服务不被终止]
查看>>
highcharts的柱状图边线
查看>>
来吧学学.Net Core之项目文件简介及配置文件与IOC的使用
查看>>
字符串类的扩展
查看>>
路由系统和模板基础
查看>>
SQL学习笔记(基本语法)
查看>>
app 进入后台进行模糊处理
查看>>
缓存使用场景
查看>>
fatal error: error writing to /tmp/ccXIKnDg.s: No space left on device
查看>>
跨域方法:JSONP、iframe
查看>>
配置 archlinux 之安装 xfce
查看>>
数据结构与算法JavaScript描述
查看>>