【创作赢红包】Java多线程:synchronized锁方法块

这篇具有很好参考价值的文章主要介绍了【创作赢红包】Java多线程:synchronized锁方法块。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

synchronized同步代码块
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用synchronized同步语句块来解决问题。看一下例子:
   
 public class ThreadDomain18 {
        public void doLongTimeTask() throws Exception {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                        "nosynchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
            }
            System.out.println();
            synchronized (this) {
                for (int i = 0; i < 100; i++) {
                    System.out.println(
                            "synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
                }
            }
        }
    }
public class MyThread18 extends Thread {
        private ThreadDomain18 td;

        public MyThread18(ThreadDomain18 td) {
            this.td = td;
        }

        public void run() {
            try {
                td.doLongTimeTask();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
public static void main(String[] args){
    ThreadDomain18 td = new ThreadDomain18();
    MyThread18 mt0 = new MyThread18(td);
    MyThread18 mt1 = new MyThread18(td);
    mt0.start();
    mt1.start();
}
运行结果,分两部分来看:
synchronized threadName = Thread-1, i = 1
synchronized threadName = Thread-1, i = 2
nosynchronized threadName = Thread-0, i = 95
synchronized threadName = Thread-1, i = 3
nosynchronized threadName = Thread-0, i = 96
synchronized threadName = Thread-1, i = 4
nosynchronized threadName = Thread-0, i = 97
synchronized threadName = Thread-1, i = 5
nosynchronized threadName = Thread-0, i = 98
synchronized threadName = Thread-1, i = 6
nosynchronized threadName = Thread-0, i = 99
synchronized threadName = Thread-1, i = 7
nosynchronized threadName = Thread-0, i = 100
...
synchronized threadName = Thread-1, i = 98
synchronized threadName = Thread-1, i = 99
synchronized threadName = Thread-1, i = 100
synchronized threadName = Thread-0, i = 1
synchronized threadName = Thread-0, i = 2
synchronized threadName = Thread-0, i = 3
...
这个实验可以得出以下两个结论:
1、 当A线程访问对象的synchronized代码块的时候,B线程依然可以访问对象方法中其余非synchronized块的部分,第一部分的执行结果证明了这一点
2、 当A线程进入对象的synchronized代码块的时候,B线程如果要访问这段synchronized块,那么访问将会被阻塞,第二部分的执行结果证明了这一点
所以,从执行效率的角度考虑,有时候我们未必要把整个方法都加上synchronized,而是可以采取synchronized块的方式,对会引起线程安全问题的那一部分代码进行synchronized就可以了。
两个synchronized块之间具有互斥性
如果线程1访问了一个对象A方法的synchronized块,那么线程B对同一对象B方法的synchronized块的访问将被阻塞,写个例子来证明一下:
 public class ThreadDomain19 {
        public void serviceMethodA() {
            synchronized (this) {
                try {
                    System.out.println("A begin time = " + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("A end time = " + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void serviceMethodB() {
            synchronized (this) {
                System.out.println("B begin time = " + System.currentTimeMillis());
                System.out.println("B end time = " + System.currentTimeMillis());
            }
        }
    }
写两个线程分别调用这两个方法:
public class MyThread19_0 extends Thread{
    private ThreadDomain19 td;
    public MyThread19_0(ThreadDomain19 td){
        this.td = td;
    }
    public void run(){
        td.serviceMethodA();
    }
}
public class MyThread19_1 extends Thread{
    private ThreadDomain19 td;
    public MyThread19_1(ThreadDomain19 td){
        this.td = td;
    }
    public void run(){
        td.serviceMethodB();
    }
}
写个main函数:
public static void main(String[] args){
    ThreadDomain19 td = new ThreadDomain19();
    MyThread19_0 mt0 = new MyThread19_0(td);
    MyThread19_1 mt1 = new MyThread19_1(td);
    mt0.start();
    mt1.start();
}
看一下运行结果:
A begin time = 1443843271982
A end time = 1443843273983
B begin time = 1443843273983
B end time = 1443843273983
看到对于serviceMethodB()方法synchronized块的访问必须等到对于serviceMethodA()方法synchronized块的访问结束之后。那其实这个例子,我们也可以得出一个结论: synchronized块获得的是一个对象锁,换句话说,synchronized块锁定的是整个对象
synchronized块和synchronized方法
既然上面得到了一个结论 synchronized块获得的是对象锁 ,那么如果线程1访问了一个对象方法A的synchronized块,线程2对于同一对象同步方法B的访问应该是会被阻塞的,因为线程2访问同一对象的同步方法B的时候将会尝试去获取这个对象的对象锁,但这个锁却在线程1这里。写一个例子证明一下这个结论:
   
 public class ThreadDomain20 {
        public synchronized void otherMethod() {
            System.out.println("----------run--otherMethod");
        }

        public void doLongTask() {
            synchronized (this) {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(
                            "synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
写两个线程分别调用这两个方法:
public class MyThread20_0 extends Thread{
    private ThreadDomain20 td;
    public MyThread20_0(ThreadDomain20 td){
        this.td = td;
    }
    public void run(){
        td.doLongTask();
    }
}
public class MyThread20_1 extends Thread{
    private ThreadDomain20 td;
    public MyThread20_1(ThreadDomain20 td){
        this.td = td;
    }
    public void run(){
        td.otherMethod();
    }
}
写个main函数调用一下,这里"mt0.start()"后sleep(100)以下是为了确保mt0线程先启动:
public static void main(String[] args) throws Exception{
    ThreadDomain20 td = new ThreadDomain20();
    MyThread20_0 mt0 = new MyThread20_0(td);
    MyThread20_1 mt1 = new MyThread20_1(td);
    mt0.start();
    Thread.sleep(100);
    mt1.start();
}
看一下运行结果:
...
synchronized threadName = Thread-0, i = 995
synchronized threadName = Thread-0, i = 996
synchronized threadName = Thread-0, i = 997
synchronized threadName = Thread-0, i = 998
synchronized threadName = Thread-0, i = 999
synchronized threadName = Thread-0, i = 1000
----------run--otherMethod
证明了我们的结论。为了进一步完善这个结论,把"otherMethod()"方法的synchronized去掉再看一下运行结果:
...
synchronized threadName = Thread-0, i = 16
synchronized threadName = Thread-0, i = 17
synchronized threadName = Thread-0, i = 18
synchronized threadName = Thread-0, i = 19
synchronized threadName = Thread-0, i = 20
----------run--otherMethod
synchronized threadName = Thread-0, i = 21
synchronized threadName = Thread-0, i = 22
synchronized threadName = Thread-0, i = 23
...
"otherMethod()"方法和"doLongTask()"方法中的synchronized块异步执行了
将任意对象作为对象监视器
总结一下前面的内容:
1、synchronized同步方法
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized同步方法中的代码
2、synchronized同步代码块
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
前面都使用synchronized(this)的格式来同步代码块,其实 Java还支持对"任意对象"作为对象监视器来实现同步的功能 。这个"任意对象"大多数是 实例变量 方法的参数 ,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:
public class ThreadDomain21 {
        private String userNameParam;
        private String passwordParam;
        private String anyString = new String();

        public void setUserNamePassword(String userName, String password) {
            try {
                synchronized (anyString) {
                    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 进入同步代码块");
                    userNameParam = userName;
                    Thread.sleep(3000);
                    passwordParam = password;
                    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 离开同步代码块");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
写两个线程分别调用一下:
public class MyThread21_0 extends Thread{
    private ThreadDomain21 td;
    public MyThread21_0(ThreadDomain21 td){
        this.td = td;
    }
    public void run(){
        td.setUserNamePassword("A", "AA");
    }
}
public class MyThread21_1 extends Thread{
    private ThreadDomain21 td;
    public MyThread21_1(ThreadDomain21 td){
        this.td = td;
    }
    public void run(){
        td.setUserNamePassword("B", "B");
    }
}
写一个main函数调用一下:
public static void main(String[] args){
    ThreadDomain21 td = new ThreadDomain21();
    MyThread21_0 mt0 = new MyThread21_0(td);
    MyThread21_1 mt1 = new MyThread21_1(td);
    mt0.start();
    mt1.start();
}
看一下运行结果:
线程名称为:Thread-0在 1443855101706 进入同步代码块
线程名称为:Thread-0在 1443855104708 离开同步代码块
线程名称为:Thread-1在 1443855104708 进入同步代码块
线程名称为:Thread-1在 1443855107708 离开同步代码块
这个例子证明了: 多个线程持有"对象监视器"为同一个对象的前提下,同一时间只能有一个线程可以执行synchronized(非this对象x)代码块中的代码
锁非this对象具有一定的优点:如果在一个类中有很多synchronized方法,这时虽然能实现同步,但会受到阻塞,从而影响效率。但如果同步代码块锁的是非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高了运行效率。
注意一下"private String anyString = new String();"这句话,现在它是一个全局对象,因此监视的是同一个对象。如果移到try里面,那么对象的监视器就不是同一个了,调用的时候自然是异步调用,可以自己试一下。
最后提一点,synchronized(非this对象x),这个对象如果是实例变量的话,指的是对象的引用, 只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的
细化synchronized(非this对象x)的三个结论
synchronized(非this对象x)格式的写法是将x对象本身作为对象监视器,有三个结论得出:
1、当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
2、当其他线程执行x对象中的synchronized同步方法时呈同步效果
3、当其他线程执行x对象方法中的synchronized(this)代码块时也呈同步效果
第一点很明显,第二点和第三点意思类似,无非一个是同步方法,一个是同步代码块罢了,举个例子验证一下第二点:
 
public class MyObject {
        public synchronized void speedPrintString() {
            System.out.println("speedPrintString__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
            System.out.println("----------");
            System.out.println("speedPrintString__releaseLock time = " + System.currentTimeMillis()+ ", run ThreadName = " + Thread.currentThread().getName());
        }
    }
ThreadDomain24中持有MyObject的引用:
public class ThreadDomain24 {
        public void testMethod1(MyObject mo) {
            try {
                synchronized (mo) {
                    System.out.println("testMethod1__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
                    Thread.sleep(5000);
                    System.out.println("testMethod1__releaseLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } 
        }
    }
写两个线程分别调用" speedPrintString() "方法和" testMethod1(MyObject mo) "方法:
public class MyThread24_0 extends Thread{
    private ThreadDomain24 td;
    private MyObject mo;
    public MyThread24_0(ThreadDomain24 td, MyObject mo){
        this.td = td;
        this.mo = mo;
    }
    public void run() {
        td.testMethod1(mo);
    }
}
public class MyThread24_1 extends Thread{
    private MyObject mo;
    public MyThread24_1(MyObject mo){
        this.mo = mo;
    }
    public void run(){
        mo.speedPrintString();
    }
}
写一个main函数启动这两个线程:
public static void main(String[] args){
    ThreadDomain24 td = new ThreadDomain24();
    MyObject mo = new MyObject();
    MyThread24_0 mt0 = new MyThread24_0(td, mo);
    MyThread24_1 mt1 = new MyThread24_1(mo);
    mt0.start();
    mt1.start();
}
看一下运行结果:
testMethod1__getLock time = 1443855939811, run ThreadName = Thread-0
testMethod1__releaseLock time = 1443855944812, run ThreadName = Thread-0
speedPrintString__getLock time = 1443855944812, run ThreadName = Thread-1
----------
speedPrintString__releaseLock time = 1443855944812, run ThreadName = Thread-1
看到"speedPrintString()"方法必须等待"testMethod1(MyObject mo)"方法执行完毕才可以执行,没有办法异步执行,证明了第二点的结论。第三点的验证方法类似,就不写代码证明了。

文章来源地址https://www.toymoban.com/news/detail-407800.html

到了这里,关于【创作赢红包】Java多线程:synchronized锁方法块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java|多线程与高并发】线程安全问题以及synchronized使用实例

    Java多线程环境下,多个线程同时访问共享资源时可能出现的数据竞争和不一致的情况。 线程安全一直都是一个令人头疼的问题.为了解决这个问题,Java为我们提供了很多方式. synchronized、ReentrantLock类等。 使用线程安全的数据结构,例如ConcurrentHashMap、ConcurrentLinkedQueue等

    2024年02月09日
    浏览(44)
  • Java关键字之synchronized详解【Java多线程必备】

    点击   Mr.绵羊的知识星球  解锁更多优质文章。 目录 一、介绍 二、特性 1. 线程安全 2. 互斥访问 3. 可重入性 4. 内置锁 三、实现原理 四、和其他锁比较 1. 优点 2. 缺点 五、注意事项和最佳实践 六、使用案例 1. 案例一 2. 案例二     synchronized是Java中最基本的同步机制之一,

    2024年01月24日
    浏览(46)
  • Java多线程(4)---死锁和Synchronized加锁流程

    目录 前言 一.synchronized 1.1概念  1.2Synchronized是什么锁? 1.3Synchronized加锁工作过程 1.4其他优化操作 二.死锁 2.1什么是死锁 2.2死锁的几个经典场景 2.3死锁产生的条件 2.4如何解决死锁 🎁个人主页:tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 🎥 本文由 tq02 原创,首发于

    2024年02月13日
    浏览(39)
  • Java并发编程(三)线程同步 上[synchronized/volatile]

    当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。 所以我们通过线程同步机制来保证线程安全,加入同步锁以避免在该线程没有完成

    2024年02月13日
    浏览(41)
  • 【并发多线程】java并发中的Synchronized关键词

    如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。 java提供了synchronized,方便我们实现上述操作。 我们举个例子,我们创建一个

    2023年04月13日
    浏览(44)
  • java八股文面试[多线程]——synchronized锁升级过程

    速记:偏向-轻量-重量 上面讲到锁有四种状态,并且会因实际情况进行膨胀升级,其膨胀方向是: 无锁——偏向锁——轻量级锁——重量级锁 ,并且膨胀方向 不可逆 一.锁升级理论. 在synchronized锁升级过程中涉及到以下几种锁.先说一下这几种锁是什么意思. 偏向锁: 只有一个

    2024年02月10日
    浏览(42)
  • java八股文面试[多线程]——Synchronized的底层实现原理

    笔试:画出Synchronized 线程状态流转 实现原理图 synchronized解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是 同步 ,也称之为”同步锁“。 synchronized的作用是保证在 同一时刻 , 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的

    2024年02月10日
    浏览(46)
  • java八股文面试[多线程]——synchronized锁升级详细流程

    偏向锁是JDK6中的重要引进,因为HotSpot作者经过研究实践发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由 同一线程多次获得 ,为了让线程获得锁的代价更低,引进了偏向锁。 偏向锁是在 单线程 执行代码块时使用的机制,如果在多线程并发的环境下(即线程

    2024年02月10日
    浏览(32)
  • 【创作赢红包】项目信息分析表

    项目的背景和问题 项目背景: 很多 父母 都 希望他们的孩子成为优秀的人 ,但是在实践中,他们的教育方式往往会出现一些与期望不符的情况。这可能是因为这些父母很大程度上是按照自己的父母的教育方式来教育孩子的,这导致 一些不太好的方式被沿袭和流传下来 。,举

    2023年04月15日
    浏览(32)
  • 【创作赢红包】python学习——【第七弹】

    上一篇文章 python学习——【第六弹】中介绍了 python中的字典操作 ,这篇文章接着学习python中的 可变序列 集合 1: 集合是python语言提供的内置数据结构,具有 无序性 (集合中的元素无法通过索引下标访问,并且每次输出时元素的排序顺序可能都不相同。), 互异性 , 确定

    2023年04月08日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包