Java之Synchronized关键字

在多线程环境中,有可能会出现多个线程同时访问同一个资源的情况,资源可以是:一个文件、变量、对象、数据库表等。而单个线程的执行过程是不可控的,可能导致结果与逾期不同。这个资源被称为“临界资源”(也称共享资源)。

1.什么情况出现线程安全问题

情况重现:

public class Main {

    public static void main(String[] args) {
//        SyncThread sync1 = new SyncThread("sync1");
//        SyncThread sync2 = new SyncThread("sync2");
//        Thread thread1 = new Thread(sync1);
//        Thread thread2 = new Thread(sync2);
//        thread1.start();
//        thread2.start();
        for (int i = 0; i < 5; i++) {
            Thread thread1 = new Thread(new SyncThread("sync1"));
            thread1.start();
        }
    }

    static class SyncThread implements Runnable {
        private String name;
        private static int count = 0;

        public SyncThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                count++;
                System.out.println(this.name+" : " + count);
            }
        }
    }
}

根据代码来看,最终输出的结果应该是5*10=50,实际输出(省略):

sync1 : 1
sync1 : 3
sync1 : 4
sync1 : 5
sync1 : 6
sync1 : 7
sync1 : 8
sync1 : 9
sync1 : 48
sync1 : 49
sync1 : 44
sync1 : 50

其输出结果不会递增出现无规律;这就是典型的线程安全问题。当前一个线程拿到count值时,其他的某些线程也同时拿到count值,同时进行某操作导致写入的值一致。

2.Synchronized解决线程安全问题

Synchronized关键字的使用场景以及锁的对象归纳如下图:(静态方法属于类本身不属于对象)
在这里插入图片描述

  1. 修饰一个代码块

public class SyncThread implements Runnable {
    private static int count;

    public SyncThread() {
        count = 0;
    }

    @Override
    public synchronized void run() {
        for (int i = 0; i < 10; i++) {
            count++;
            System.out.println(Thread.currentThread().getName() + " : " + count);
        }
    }
}


使用:

 SyncThread run = new SyncThread();
        Thread thread1 = new Thread(run, "sync1");
        Thread thread2 = new Thread(run, "sync2");
        thread1.start();
        thread2.start();

输出:

sync1 : 1
sync1 : 2
sync1 : 3
sync1 : 4
sync1 : 5
sync1 : 6
sync1 : 7
sync1 : 8
sync1 : 9
sync1 : 10
sync2 : 11
sync2 : 12
sync2 : 13
sync2 : 14
sync2 : 15
sync2 : 16
sync2 : 17
sync2 : 18
sync2 : 19
sync2 : 20

一个线程访问一个对象中的synchronized(this)(锁住的该对象)同步代码块时,其他试图访问该对象的线程将被阻塞,直到该对象锁释放

修改上面的代码:

 SyncThread run1 = new SyncThread();
  SyncThread run2 = new SyncThread();
        Thread thread1 = new Thread(run1, "sync1");
        Thread thread2 = new Thread(run2, "sync2");
        thread1.start();
        thread2.start();

得到的输出完全不一致:

sync1 : 2
sync2 : 2
sync2 : 4
sync2 : 5
sync2 : 6
sync2 : 7
sync2 : 8
sync2 : 9
sync2 : 10
sync2 : 11
sync2 : 12
sync1 : 12
sync1 : 13
sync1 : 14
sync1 : 15
sync1 : 16
sync1 : 17
sync1 : 18
sync1 : 19
sync1 : 20

其余的使用类似,不一一列出。

总结:

修饰一个代码块:

  1. 一个线程访问一个对象中的synchronized(this)(锁住的该对象)同步代码块时,其他试图访问该对象的线程将被阻塞
  2. 一个对象只有一个锁
  3. 当一个线程访问一个对象的synchronized(this)同步代码块时,另一个线程仍然可以访问该对象的非synchronized(this)同步代码块
  4. 当没有明确对象作为锁时,只想让一段代码同步时,可以创建一个特殊的对象来充当锁;零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
byte[] lock = new byte[0];
public void method(){
	synchronized(lock){
	}
}

修饰一个方法:

  1. public synchronized void method(){}
  2. synchronized 关键字不能继承
  3. 定义接口方法时不能使用synchronized关键字
  4. 构造方法不能使用synchronized关键字,构造方法中可以使用synchronized代码块来进行同步

修饰一个静态方法:(静态方法属于类不属于对象)

  1. synchronized锁静态方法即锁类的所有对象

修饰一个类:

  1. synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。

参考自:
https://blog.csdn.net/xiao__gui/article/details/8188833
https://www.cnblogs.com/dolphin0520/p/3923737.html
http://www.importnew.com/21866.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值