资讯专栏INFORMATION COLUMN

java多线程:synchronized

entner / 3170人阅读

摘要:非静态方法以及方法内部的代码块持有的是同一个对象锁,它们是同步执行的。可重入锁使用时,当一个线程请求一个对象锁时,再次请求该锁是可以立即得到的。出现异常,会自动释放锁同步方法与同步代码块作用于整个方法,可能引起方法执行效率下降。

synchronize可以在多个线程操作同一个成员变量或者方法时,实现同步(或者互斥)的效果。
synchronized可以作用于方法,以及方法内部的代码块。

//1 
synchronized void method(){}
   //2
static synchronized void method(){}
//3
synchronized void method(){
    synchronized(锁对象){
    }
}
//4
static synchronized void method(){
    synchronized(锁对象){
    }
}
锁对象

那么在上面的示例中,它们分别持有的锁对象是谁?
synchronized作用于非静态方法以及非静态方法内部的代码块,持有的是当前类的对象的锁,并且是同一个锁。作用于静态方法及其内部的代码块,持有的是当前类的Class对象的锁,并且和非静态方法不是同一个锁。
通过代码来验证。

public class SynchronizedTest {

    private synchronized void test1(){
        for (int x = 0; x < 5; x++) {

            System.out.println("test1---"+x);
        }
    }
    private  void test2(){
        synchronized(this) {
            for (int x = 0; x < 5; x++) {

                System.out.println("---test2---"+x);
            }
        }
    }

    private static synchronized void test3(){
        for (int x = 0; x < 5; x++) {

            System.out.println("------test3---"+x);
        }
    }

    private static  void test4(){
        synchronized (SynchronizedTest.class){
            for (int x = 0; x < 5; x++) {

                System.out.println("---------test4---"+x);
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test2();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test3();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test4();
            }
        }).start();
    }
}

执行结果

test1---0
------test3---0
test1---1
------test3---1
test1---2
test1---3
------test3---2
test1---4
------test3---3
------test3---4
---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
---------test4---0
---------test4---1
---------test4---2
---------test4---3
---------test4---4

test1和test2不会交叉执行,test3和test4也不会交叉执行。非静态方法以及方法内部的代码块持有的是同一个对象锁,它们是同步执行的。静态方法和内部的代码块持有的是当前类的Class对象锁,它们是同步执行的。而静态方法和非静态方法持有的不是同一个锁,它们是异步的。

String作为锁

字符串常量作为锁,会有什么结果?

        final String a = "100";
        final String b = "100";
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a){
                    while (true){
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        },"thread-a").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (b){
                    while (true){
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        },"thread-b").start();

这里字符串a和b虽然是两个对象,但是声明b时,会将字符串常量池中已存在的a的值直接赋给b。这样a和b其实是一样的。这样线程thread-a和thread-b同时抢占同一个锁,一旦一个线程抢到该锁,另一个线程就再也获取不到该锁。

synchronized不具有继承性

自己覆盖了父类被synchronized修饰的方法,子类方法如果需要同步性,也需要用synchronized修饰。
定义子类Sub继承自SynchronizedTest

class Sub extends SynchronizedTest{
    @Override
    public void test2() {
        //super.test2();
        for (int x = 0; x < 15; x++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

然后开启两个线程调用Sub的test2()方法。

final  Sub sub = new Sub();
       new Thread(new Runnable() {
           @Override
           public void run() {
               sub.test2();
           }
       },"Sub---A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                sub.test2();
            }
        },"Sub---B").start();

打印结果

Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B

可见Sub的test2()方法并没有同步性。也就是synchronized不能被继承。

可重入锁

使用synchronized时,当一个线程请求一个对象锁时,再次请求该锁是可以立即得到的。

public class SynchronizedTest {

    private synchronized void test1(){
        for (int x = 0; x < 5; x++) {

            System.out.println("test1---"+x);
        }
        test2();
    }
    public  void test2(){
        synchronized(this) {
            for (int x = 0; x < 5; x++) {

                System.out.println("---test2---"+x);
            }
        }
    }
    
    public static void main(String[] args) {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test1();
            }
        }).start();
        
    }
 }

在方法test1()中调用方法test2(),并不需要等待锁,而是立即获取到锁。
把子类Sub的test2()方法也改成synchronized修饰。并在其内部调用父类的test2()方法。能获得锁吗?

class Sub extends SynchronizedTest{
    @Override
    public synchronized void test2() {
        //调用父类的同步方法
        super.test2();
        for (int x = 0; x < 15; x++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

打印结果

---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
Sub---A
Sub---A
Sub---A
Sub---A

可以看到父类方法执行完毕,子类的方法立即执行。可见,在子父类的继承关系中,也支持可重入锁这个特性。

出现异常,会自动释放锁 同步方法与同步代码块

synchronized作用于整个方法,可能引起方法执行效率下降。建议将方法内部需要同步的代码用synchronized修饰,也就是synchronized代码块。

多个同步锁对象

在一个类中假如有多个同步方法,它们之间并不需要互斥。那么使用同一个锁,会大大降低效率。可以定义多个同步锁对象。

 Object obj1 = new Object();
 Object obj2 = new Object();
    
 public void method1(){
  synchronized(obj1){

}
}

 public void method2(){
  synchronized(obj2){

}
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77669.html

相关文章

  • Java线程学习(二)synchronized关键字(1)

    摘要:转载请备注地址多线程学习二将分为两篇文章介绍同步方法另一篇介绍同步语句块。如果两个线程同时操作对象中的实例变量,则会出现非线程安全,解决办法就是在方法前加上关键字即可。 转载请备注地址: https://blog.csdn.net/qq_3433... Java多线程学习(二)将分为两篇文章介绍synchronized同步方法另一篇介绍synchronized同步语句块。系列文章传送门...

    xuxueli 评论0 收藏0
  • Java线程学习(二)synchronized关键字(2)

    摘要:关键字加到非静态方法上持有的是对象锁。线程和线程持有的锁不一样,所以和运行同步,但是和运行不同步。所以尽量不要使用而使用参考多线程编程核心技术并发编程的艺术如果你觉得博主的文章不错,欢迎转发点赞。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) J...

    Batkid 评论0 收藏0
  • Java 线程核心技术梳理(附源码)

    摘要:本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,的使用,定时器,单例模式,以及线程状态与线程组。源码采用构建,多线程这部分源码位于模块中。通知可能等待该对象的对象锁的其他线程。 本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,lock的使用,定时器,单例模式,以及线程状态与线程组。 写在前面 花了一周时...

    Winer 评论0 收藏0
  • Java线程基础(一)——线程与锁

    摘要:一线程的基本概念单线程简单的说,单线程就是进程中只有一个线程。多线程由一个以上线程组成的程序称为多线程程序。当线程调用完方法进入后会自动释放锁,线程获得锁。 一、线程的基本概念 1.1 单线程 简单的说,单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。 Java示例: public class SingleThrea...

    WelliJhon 评论0 收藏0
  • Java锁机制了解一下

    摘要:底层是是通过对象,对象有自己的对象头,存储了很多信息,其中一个信息标示是被哪个线程持有。当一个线程执行的代码出现异常时,其所持有的锁会自动释放。 前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchronized 显式Lock 不得不唠...

    hyuan 评论0 收藏0
  • JAVA线程机制解析-volatile&synchronized

    摘要:当一个线程持有重量级锁时,另外一个线程就会被直接踢到同步队列中等待。 java代码先编译成字节码,字节码最后编译成cpu指令,因此Java的多线程实现最终依赖于jvm和cpu的实现 synchronized和volatile 我们先来讨论一下volatile关键字的作用以及实现机制,每个线程看到的用volatile修饰的变量的值都是最新的,更深入的解释就涉及到Java的内存模型了,我们...

    dendoink 评论0 收藏0

发表评论

0条评论

entner

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<