并发编程之synchronized

本文详细解析了Java中synchronized关键字的三种使用方式:修饰普通方法、静态方法及代码块,并通过示例程序阐述了它们如何实现线程同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  并发编程一个很棘手的问题就是对临界资源的访问,当大量线程同时对一个不安全的临界资源访问时,为了不出现与我们期望不符的结果,我们在写程序的时候需要考虑做适当的线程同步。其中synchronized关键字就是对线程同步的一种手段,synchronized可以修饰普通方法,静态方法,代码块。

  1.1修饰普通方法

在我初次接触到synchronized关键字的时候,当我使用synchronized修饰普通方法时,我有一个疑惑就是synchronized锁住的是方法,还是这个方法所在的对象,也就是说,一个类中有多个被synchronized修饰的方法,如果我有两个线程,不管synchronized锁住的是方法还是整个对象,两个线程是无法同时访问同一个方法的,但是两个线程是否可以同时访问不同的被synchronized修饰的方法呢,我们通过下面的一个例子来解答我最初的疑惑。

package Synchronized;

/**
 * Created by Jackie on 2017/8/10.
 *
 */
public class Synchronzied {

    public synchronized void methodOne(){
        System.out.println(Thread.currentThread() + "methodOne");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodTwo(){
        System.out.println(Thread.currentThread() + "methodTwo");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        Synchronzied synchronzied = new Synchronzied();

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodOne();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();

        System.out.println("test");

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodTwo();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();
    }
}

  程序就是开两个线程同时访问两个被synchronized修饰的方法,每个方法体都会将当前线程休眠五秒,Java中sleep并不会丢失锁,而wait方法会丢失当前线程的锁。最终程序运行结果是当第一个线程访问methodOne时,第二个线程并不能立即执行methodTwo。当第一个线程运行五秒过后,第二个线程就能运行第二个方法了。从而可以分析出线程访问被synchronized修饰的普通方法时,获取的是整个对象锁,而不是方法锁(好像没这个锁),但是synchronized锁的粒度很大,比如,有两个方法操作操作的是不同的临界资源,不同的线程同时执行两个方法时并不会影响程序的最终结果,此时synchronized会造成程序效率的丢失,所以我们在写程序的时候不能滥用synchronized,此外还有一种很优雅的锁同步机制,那就是Lock,后面我可能会写一篇Lock的用法。

  1.2修饰静态方法

  类中的静态方法时属于类的,普通方法时属于实例对象的,那么synchronized修饰静态方法和修饰普通方法有什么不同呢?来看下面的一个例子:
/**
 * Created by Jackie on 2017/8/10.
 *
 */
public class Synchronzied {

    public synchronized static void methodOne(){
        System.out.println(Thread.currentThread() + "methodOne");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodTwo(){
        System.out.println(Thread.currentThread() + "methodTwo");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        Synchronzied synchronzied = new Synchronzied();

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodOne();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();

        System.out.println("test");

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodTwo();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();
    }
}
  和上面的程序基本上一样,只是在methodOne上添加了一个static,程序运行结果是,两个不同的线程可以同时访问两个方法,原理也很简单,静态方法是属于类,所以锁住的是类,普通方法是属于实例对象,锁住的是对象,这两个是不同的锁,所以不同的线程可以同时访问这两个方法。我们还可以修改上面的代码来验证我们的结论。
/**
 * Created by Jackie on 2017/8/10.
 *
 */
public class Synchronzied {

    public synchronized static void methodOne(){
        System.out.println(Thread.currentThread() + "methodOne");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodTwo(){
        System.out.println(Thread.currentThread() + "methodTwo");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        Synchronzied synchronzied = new Synchronzied();

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodOne();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();

        System.out.println("test");

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodTwo();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                synchronzied.methodOne();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();
    }
}
  上面的代码中添加了一个线程,这个线程和第一个线程一样需要执行静态方法,所以此时有两个不同的线程需要执行静态方法,最终的运行结果也符合预期,两个不同的线程执行相同的静态方法时不同同时执行。

  1.3修饰代码块

  我们还是对上面的代码进行改动
/**
 * Created by Jackie on 2017/8/10.
 *
 */
public class Synchronzied {

    public void methodOne() {
        System.out.println(Thread.currentThread() + "methodOne");
        synchronized (this){
            System.out.println("synchronized...... methodOne");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void methodTwo() {
        System.out.println(Thread.currentThread() + "methodTwo");
        synchronized (this){
            System.out.println("synchronized...... methodTwo");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Synchronzied synchronzied = new Synchronzied();

        new Thread() {
            @Override
            public void run() {
                synchronzied.methodOne();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                synchronzied.methodTwo();
                System.out.println(Thread.currentThread() + " end");
            }
        }.start();
    }
}
  synchronized锁住的是同一个对象this,所以不能同时进入不同的被synchronized修饰的代码块,若是synchronized锁住的是不同的对象,那么不同的线程可以同时访问不同的被synchronized修饰的的代码块了,套路都是差不多了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值