Java中Synchronized关键字的基本使用方法

Java中Synchronized关键字的基本使用方法

1.简介
Synchronized是java的关键字,synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到。

Synchronized关键字细分的话,主要的修饰对象有以下几种:
1)修饰一个代码块,其作用的范围是Synchronized后面{}括起来的代码,作用的对象是调用这个代码块的实例对象,对给定实例对象加锁,进入同步代码块之前要获得给定实例对象的锁。如:

public void test() {
		// 同步代码块,this为给定实例对象
        synchronized (this) {
           // 同步代码块中的内容
        }
}

2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的实例对象;如:

// 同步方法
public synchronized void test() {
		
}

3)修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有实例对象。如:

public synchronized static void test() {
		
}

4)修饰一个类,其作用的范围是synchronized后面{}括起来的部分,作用的对象是这个类的所有实例对象,类似于1与3的结合,与1相比,1只作用于对象的单个实例,而4则作用于对象的全部实例。如:

public void test() {
		// 同步代码块,作用的对象为Test类的所有实例对象。
        synchronized (Test.Class) {
           // 同步代码块中的内容
        }
}

2.Synchronized修饰代码块

public class SyncThread implements Runnable {

    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        Thread thread1 = new Thread(syncThread, "SyncThread1");
        Thread thread2 = new Thread(syncThread, "SyncThread2");
        thread1.start();
        thread2.start();
    }

    private static int count;

    public SyncThread() {
        count = 0;
    }

    public void run() {
    	// 其他代码。。。
        //一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(100);
                    notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
       // 其他代码。。。
    }
    public int getCount() {
        return count;
    }
}

在这里插入图片描述

当两个并发线程(thread1和thread2)访问同一个实例对象(syncThread)中的synchronized代码块时,在同一时间只能有一个线程可以执行, 另一个线程会收到阻塞,必须等待当前线程执行完整个代码块以后才能接着执行该代码块。
Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的实例对象,只有执行完该代码块才能释放该对象锁,下一个线程才能的到并执行并锁定该对象。

我们可以做一个对比,接下来稍微修改一下实例对象(syncThread):

        SyncThread syncThread1 = new SyncThread();
        SyncThread syncThread2 = new SyncThread();
        Thread thread1 = new Thread(syncThread1, "SyncThread1");
        Thread thread2 = new Thread(syncThread2, "SyncThread2");
        thread1.start();
        thread2.start();

在这里插入图片描述
线程thread1和线程thread2分别创建了两个SyncThread的实例对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码块,而线程thread2执行的是syncThread2对象中的synchronized代码块。
我们知道synchronized锁定的是实例对象,这时会有两把锁分别锁定SyncThread这个对象的syncThread1实例和syncThread2实例,这是两把锁,所以互不干扰的,不形成互斥,两个线程可以同时执行。

3.Synchronized修饰方法

public class SyncThread implements Runnable {

    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        Thread thread1 = new Thread(syncThread, "SyncThread1");
        Thread thread2 = new Thread(syncThread, "SyncThread2");
        thread1.start();
        thread2.start();
    }

    private static int count;

    public SyncThread() {
        count = 0;
    }

    public synchronized void method() {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + (count++));
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void run() {
		method();
    }
    public int getCount() {
        return count;
    }
}

synchronized修饰方法和synchronized修饰代码块本质其实没什么区别,synchronized修饰代码块是因为,在一些情况下,我们编写的方法,方法体可能比较大,同时可能存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会造成不小的损失,所以我们使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。

synchronized修饰方法可以参照上文的synchronized修饰代码块。

4.Synchronized修饰静态方法

public class SyncThread implements Runnable {

    public static void main(String[] args) {
        SyncThread syncThread1 = new SyncThread();
        SyncThread syncThread2 = new SyncThread();
        Thread thread1 = new Thread(syncThread1, "SyncThread1");
        Thread thread2 = new Thread(syncThread2, "SyncThread2");
        thread1.start();
        thread2.start();
    }

    private static int count;

    public SyncThread() {
        count = 0;
    }

    public static synchronized void method() {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + (count++));
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void run() {
		method();
    }
    public int getCount() {
        return count;
    }
}

在这里插入图片描述
当synchronized作用于静态方法时,就是相当于当前类的class对象锁,作用于类的所有实例对象,静态方法不专属于任何一个实例对象。
需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是类的class对象,而访问非静态synchronized方法占用的锁是当前类的实例对象锁。如:

public class SyncThread implements Runnable{

    public static void main(String[] args) {
        Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
        Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
        thread1.start();
        thread2.start();
    }

    private static int count;

    public SyncThread() {
        count = 0;
    }

    public static synchronized void method() {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }

    public synchronized void test(){
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + (count++));
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void run() {
        String threadName = Thread.currentThread().getName();
        if (threadName.equals("SyncThread1")) {
            method();
        } else if (threadName.equals("SyncThread2")) {
            test();
        }
    }

}

在这里插入图片描述

5.Synchronized修饰类

public class SyncThread implements Runnable {

    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        Thread thread1 = new Thread(syncThread, "SyncThread1");
        Thread thread2 = new Thread(syncThread, "SyncThread2");
        thread1.start();
        thread2.start();
    }

    private static int count;

    public SyncThread() {
        count = 0;
    }

    public void method() {
    	synchronized (SyncThread.class) {
	        for (int i = 0; i < 5; i++) {
	            try {
	                System.out.println(Thread.currentThread().getName() + ":" + (count++));
	                Thread.sleep(100);
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	        }
        }
    }

    public void run() {
		method();
    }
    public int getCount() {
        return count;
    }
}

synchronized修饰类和synchronized修饰静态方法本质其实没什么区别,相当于synchronized修饰代码块,但是其作用范围是类的全部实例对象。

synchronized修饰类可以参照上文的synchronized修饰静态方法。

6.总结一下:
synchronized虽然细分修饰了4种对象:代码块,方法,静态方法,类
但是其本质锁住的对象只有两种:调用的类的实例对象,类的全部实例对象。
代码块和方法只锁住了调用的类的实例对象,我们可以同时调用该类中非synchronized方法。
静态方法和类锁住了类的全部实例对象,我们可以同时调用该类的非静态synchronized方法。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值