Java多线程--浅谈synchronized

Java多线程–synchronized

文章主要讲解我对synchronized了解的一些特性和基本用法,如有错误欢迎留言指出。

首先我们先了解一下多线程和原子性

多线程:是指一个应用程序同时执行多个任务,一般来说一个任务就是一个线程 ,而一个应用程序有一个以上的线程我们称之为多线程。
那么问题就来了,当多个线程同时调用或处理共享数据时,如果共享的数据的原子性被破坏,线程同步的安全隐患也就暴露出来了。
原子性:是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。

举个线程同步安全隐患的例子
如果多个处理器同时对共享变量进行读改写操作(如:i++),共享变量就会被多个处理器同时进行操作,这样读改写操作就不是原子的
这里写图片描述 所以,这时候让我们来了解一下 synchronized
synchronized 关键字:当它用来修饰类、方法或者代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。

首先,先说一下synchronized作用的基本原理

这里写图片描述

我们暂且用原点代表每一个线程,假设现在线程A正在调用synchronized修饰的方法或代码块时,由于同一时刻最多只有一个线程执行这段代码(每个对象只有一个锁,谁拥有锁谁就可以运行该段代码),其余的线程如果想调用改方法,只能进入锁池等待锁的释放,当线程A执行完该段代码释放后,在锁池的所有进程将通过竞争的方式来获得该对象锁,成功获得对象锁的线程将进入方法,其余的继续留在锁池等待下一次锁的释放。

synchronized的基本用法及加锁对象
synchronized修饰用法有4种类型:类、非静态方法、静态方法以及代码块。他们的加锁对象和声明如图
这里写图片描述

接下来我通过一个简单的买票例子来了解synchronized

public class synTest{

public static void main(String[] args) {

    //例子1  相同对象
    Ticket ticket=new Ticket(10);
    Thread t1=new Thread(ticket);
    t1.start();
    Thread t2=new Thread(ticket);
    t2.start();
    Thread t3=new Thread(ticket);
    t3.start();
    //例子2  不同对象
    //Ticket tk1=new Ticket(5);
    //Ticket tk2=new Ticket(5);
    //Thread t1=new Thread(tk1);
    //t1.start();
    //Thread t2=new Thread(tk2);
    //t2.start();

    }
}
//车站购票
class Ticket implements Runnable
{
    //车票总数
    int sum;
    //当前票数
    int n=1;
    public Ticket(int x)
    {
        this.sum=x;
    }
    @Override
    public void run() {
    //TODO Auto-generated method stub
        while(true)
        {
            System.out.print("线程"+Thread.currentThread().getName()+"等待中  \t");
            synchronized(this)
            {
                System.out.print("线程"+Thread.currentThread().getName()+"竞争成功");
                if(n<=sum)
                {
                    System.out.println("当前是线程"+Thread.currentThread().getName()+"出售第"+n+"张票   ");
                    n++;
                }else{
                    System.out.println("票已售完,线程"+Thread.currentThread().getName()+"结束");
                    break;
                }
            }
        }
    }
}



例子一的运行结果是

这里写图片描述

例子二的运行结果是
这里写图片描述

从这两个例子可以看出来

  1. 当多个线程运行到被synchronized修饰的代码块时,线程间通过竞争决出一个线程获得锁并执行该段代码,而其余的将在被synchronized修饰的代码块外面继续等候。
  2. 同一对象的不同进程只能相互争取一把锁(例1),而不同对象的进程调用被修饰的方法时,由于一个对象只有一把锁,不同对象的进程争取相对对象的锁。即不同对象的线程不会相互竞争。
补充
当我们把synchronized修饰在不同地方(如方法),synchronized的作用域也随着改变。
无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
java中每个对象都可以作为锁,这是synchronized实现同步的基础
每个对象只有一个锁,谁拥有锁谁就可以运行该段代码
synchronized锁住的是对象,而不是代码块
synchronized的缺陷:
当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,这对高并发的系统很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。
同步块的情况与同步方法类似,只是同步块将同步控制的粒度缩小,这样能够更好的发挥多线程并行执行的效率。这也是为什么用synchronized时,能用修饰代码块解决的问题就绝不修饰方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值