java多线程编程(一)——synchronized关键字

学习java多线程的小伙伴对synchronized关键字一定不陌生,那么我们什么时候需要使用synchronized?synchronized解决了哪些问题?synchronized存在哪些不足?我们带着今天这些问题开始synchronized的学习

1、为什么要用synchronized?

场景1: 2个卖票员共同来卖10张票,代码如下

package cn.iocoder.demo0424.thread;

/**
 * @author :leichunhui
 * @date :Created in 2020/5/24 10:17
 * @description:售票系统
 * @modified By:
 * @version: 0.1$
 */
public class SellTicket {
    //票的总数
    private int ticketNum=10;
    public void sellTicket(){
        System.out.println(Thread.currentThread().getName() +"——————开始执行————————");
        try {
            while (ticketNum > 0) {
                System.out.println("卖票员" + Thread.currentThread().getName() + ", 卖了第" + ticketNum + "张票");
                ticketNum--;
                Thread.sleep(100);
            }
            System.out.println("票卖完了");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +"——————执行结束————————");
    }
}
package cn.iocoder.demo0424.thread;

/**
 * @author :leichunhui
 * @date :Created in 2020/5/24 10:21
 * @description:卖票员A
 * @modified By:
 * @version: 0.1$
 */
public class ThreadA extends Thread{
    SellTicket sellTicket=new SellTicket();
    public ThreadA(SellTicket sellTicket){
        super();
        this.sellTicket=sellTicket;
    }
    @Override
    public void run(){
        sellTicket.sellTicket();
    }

    public static  void main(String[] args){
        SellTicket sellTicket=new SellTicket();
        ThreadA threadA1=new ThreadA(sellTicket);
        ThreadA threadA2=new ThreadA(sellTicket);
        threadA1.start();
        threadA2.start();
    }
}

 

运行结果如下

Thread-0——————开始执行————————
卖票员Thread-0, 卖了第10张票
Thread-1——————开始执行————————
卖票员Thread-1, 卖了第9张票
卖票员Thread-1, 卖了第8张票
卖票员Thread-0, 卖了第8张票
卖票员Thread-0, 卖了第6张票
卖票员Thread-1, 卖了第5张票
卖票员Thread-1, 卖了第4张票
卖票员Thread-0, 卖了第3张票
卖票员Thread-1, 卖了第2张票
卖票员Thread-0, 卖了第2张票
票卖完了
票卖完了
Thread-0——————执行结束————————
Thread-1——————执行结束————————

 

可以看出有些票是被两个售票员同时卖出的,这个事情如果在12306系统中发生,那估计就要“杀个程序员祭天了”。当然这种事情在真实卖票系统中几乎不可能出现。那么这个问题如何能够通过synchronized解决呢?

 将 SellTicket添加锁,代码如下

package cn.iocoder.demo0424.thread;

/**
 * @author :leichunhui
 * @date :Created in 2020/5/24 10:17
 * @description:售票系统
 * @modified By:
 * @version: 0.1$
 */
public class SellTicket {
    //票的总数
    private int ticketNum=10;
    synchronized public void sellTicket(){
        System.out.println(Thread.currentThread().getName() +"——————开始执行————————");
        try {
            while (ticketNum > 0) {
                System.out.println("卖票员" + Thread.currentThread().getName() + ", 卖了第" + ticketNum + "张票");
                ticketNum--;
                Thread.sleep(100);
            }
            System.out.println("票卖完了");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +"——————执行结束————————");
    }
}

 

当然了,synchronized关键字除了添加在方法上,也可以添加在类上和代码块上,在这里就不再展示了。重新运行,结果如下

Thread-1——————开始执行————————
卖票员Thread-1, 卖了第10张票
卖票员Thread-1, 卖了第9张票
卖票员Thread-1, 卖了第8张票
卖票员Thread-1, 卖了第7张票
卖票员Thread-1, 卖了第6张票
卖票员Thread-1, 卖了第5张票
卖票员Thread-1, 卖了第4张票
卖票员Thread-1, 卖了第3张票
卖票员Thread-1, 卖了第2张票
卖票员Thread-1, 卖了第1张票
票卖完了
Thread-1——————执行结束————————
Thread-0——————开始执行————————
票卖完了
Thread-0——————执行结束————————

 

可以看出了,卖票工作全由“Thread-1”做了,为什么会这样呢?

 

2、synchronized实现原理

非线程安全问题主要原因有两方面:

 1)存在共享数据;

 2)多个线程共同操作共享数据;

而通过关键字synchronized保证同一时刻只有一个线程可以获取对象锁【不管synchronized加在类、方法还是代码块上】,确保了线程互斥的访问同步代码,从而实现了线程安全。

代码示例中线程Thread-1调用SellTicket对象加synchronized关键字的sellTicket方法时,线程Thread-1就获得了sellTicket方法锁,其实就是获得了SellTicket对象锁,所以线程Thread-0必须等线程Thread-1执行完毕才可以调用sellTicket方法。

 

3、synchronized缺点

有小伙伴会问了,synchronized既然这么好用是不是所有的多线程不安全线程都可以通过synchronized关键字来解决?当然不是了,就像人一样,人人无完人,synchronized也有其自身缺点

1)效率低:   

  • 锁的释放情况少,只在程序正常执行完成和抛出异常时释放锁;
  • 试图获得锁是不能设置超时;
  • 不能中断一个正在试图获得锁的线程;

2)无法知道线程是否成功获取到锁;

 

当然了,多线程的知识还有很多,这里只开了一个简单的头,后面我们继续一块去探索多线程的奥秘!

 

posted @ 2020-05-24 12:13  沉默小和尚  阅读(11)  评论(0编辑  收藏  举报