java synchronized关键字

本文详细介绍了Java中的synchronized关键字,包括其作用、用法及特点。synchronized关键字可用于实现线程间的互斥访问、保证共享变量的修改可见性和解决重排序问题。文章通过示例代码演示了synchronized在修饰普通方法、静态方法和代码块中的具体应用。

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

synchronized关键字用于多线程同步,其具有以下作用:
1. 确保线程互斥的访问同步代码
2. 保证共享变量的修改能够及时可见
3. 有效解决重排序问题

其主要有三种用法:
1. 修饰普通方法
2. 修饰静态方法
3. 修饰代码块

测试代码:

public class C{

    private static ArrayList<Integer> list=new ArrayList<>();

    public void noSyn() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+"no Syn");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }

    public synchronized void normalSyn1() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+"normal Syn1 ");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }

    public static void noSynStatic() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+"no Syn static");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }

    public static synchronized void staticSyn() {
        for(int i=0;i<5;++i){
            //list.add(i);
            System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }

    public void staticString1(){
        synchronized (list) {
            for(int i=0;i<5;++i){
                list=new ArrayList<>();
                System.out.println(Thread.currentThread().getName()+" "+"static integer1 "+list);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }

    public void staticString2(){
        synchronized (list) {
            for(int i=0;i<5;++i){
                System.out.println(Thread.currentThread().getName()+" "+"static integer2 "+list);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }

    public synchronized void normalSyn2() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+"normal Syn2");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }
    }
}

其特点如下:
1.synchronized修饰普通方法methodA时,在不同线程中使用同一对象调用方法methodA,这样会导致先获取该对象锁的线程先执行,其他线程受阻塞。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.normalSyn();  
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.normalSyn();
            }
        }).start();

    }

这里写图片描述

2.synchronized修饰普通方法methodA、methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取对象锁的线程先执行,其他线程受阻塞。这表明,synchronized修饰普通方式时,锁住的是对象。当某一个线程获取到该对象锁时,其他线程就不能执行需要该对象锁的方法(即synchronized修饰的普通方法,代码块上为this的synchronized代码块)

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.normalSyn1(); 
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.normalSyn2();
            }
        }).start();

    }

结果:
这里写图片描述

3.synchronized修饰普通方法methodA时,在不同线程中使用不同对象调用该方法,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,不同对象有不同的对象锁,互不影响。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.normalSyn1(); 
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c1.normalSyn1();
            }
        }).start();

结果:
这里写图片描述

4.synchronized修饰普通方法methodA时,在不同线程中同一对象,在线程A中调用methodA,在线程B中调用非synchronized的方法methodB,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,没有锁的方法不会受到对象锁影响。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.normalSyn1(); 
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.noSyn();
            }
        }).start();

    }

结果:
这里写图片描述

5.synchronized修饰静态方法时,在不同线程中使用该类的不同对象调用该方法时,这样会导致先获取类锁的线程先执行,其他线程阻塞。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.staticSyn();
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c1.staticSyn();
            }
        }).start();

    }

结果:
这里写图片描述

6.synchronized修饰普通方法methodA、静态方法methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。因为普通方法锁的是对象,静态方法锁的是类,两者互不干扰。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.staticSyn();
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.normalSyn1();
            }
        }).start();

    }

结果:
这里写图片描述

7.在方法methodA中用synchronized修饰代码块上的静态变量,synchronized修饰静态方法methodB,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。这表明虽然synchronized修饰的是静态变量,但它锁的是该变量;而synchronized修饰静态方式锁的是类,这样它们还是会互不影响。所以在synchronized锁变量时,必须要注意其在其他方法中的控制。
C类中方法staticSyn有所改变:

public static synchronized void staticSyn() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }
public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.staticString2();
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.staticSyn();
            }
        }).start();

    }

结果:
这里写图片描述

8.在方法methodA中用synchronized修饰代码块上的静态变量,在方法methodB中用synchronized修饰代码块上的同样的静态变量,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取类锁的线程先执行,其他线程阻塞。前提是在先执行的线程中没有改变该静态变量的内存地址。若改变了,那么线程会按CPU调度运行。因为synchronized锁的是变量的那个内存位置,若后面改变了该变量的内存位置,在其他方法中比对当前位置与被锁位置不一致,则会执行。

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.staticString1();      
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.staticString2();
            }
        }).start();

    }

结果:
这里写图片描述

顺序改变一下:(先执行的线程没有改变地址)

public static void main(String []ags) throws Exception{
        final C c=new C();
        final C c1=new C();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                c.staticString2();          
            }
        }).start();
        new Thread(new Runnable() {         
            @Override
            public void run() {
                // TODO 自动生成的方法存根       
                c.staticString1();
            }
        }).start();

    }

结果:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值