synchronized,同步

什么是synchronized

1.什么是synchronized:

一种使线程同步的语法。

2.为什么要用synchronized:

如果不用synchronized,那么当多个线程调用同一个任务时,同一时刻不同的线程进入同一个方法中,此时将会造成数据预期之外的错误。

使用synchronized的话可以保证再同一时间只有一个线程可以使用一个方法,或者执行一段代码。

怎么用

3.怎么用synchronized:
两种方式:
①使用同步代码块
②使用同步方法。

先说啥是同步代码块,上代码

/**
*使用同步代码块来🔒东西。
**/

static Object obj = new Object();
public void run(){
        for(int i=0;i<10;i++){
            同步代码块,一次只允许一个线程进入该代码块
            synchronized (obj){
            //锁住的是obj
            // 所以实现了同步,一次只能有线程卖票,一个线程卖完一张票后,隐式解锁,其他线程可以卖票
                  if(tickets>0) {
                    try {
                        System.out.println(Thread.currentThread().getName() + "还剩" + tickets + "张票");
                        tickets--;
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

当线程第一次调用这个任务的时候呢,会执行完这个任务后把这个对象上锁,

然后下一次线程进来的时候就会停在synchronized前面 进行一次判定,看看这个 this 就是这个线程的对象 上了锁没,

如果上了锁,代表下面有线程再执行这段代码,那么就会等着他执行完开锁,开了锁再和其他的线程一起抢这个锁。

我们可以把多个线程的对象整成一个,这个后面会讲

这样做可以让被锁住的代码块同时只有一个线程操作,但还是会造成一个问题,就是虽然这个代码块同时只有一个线程操作,但是这个方法里面还是会进多个线程,如果想要同一时刻只有一个线程进入方法,我们就需要用到同步方法。

啥是同步方法呢?
我们都知道当我们创建一个新的线程时,我们会实现 Runnable 接口,并重写 run 方法,那么我们再run方法中把这个要执行的任务单独的写出来并用synchronized修饰。这个被修饰的方法就上了锁。

上代码

/**
*这是用同步方法来🔒,区别就在于提出那个方法  然后用synchronized修饰方法。
**/


public void run(){
        for(int i=0;i<10;i++){
            sale();
        }
    }
    synchronized public void sale(){  //同步方法,表示同一时刻只能有一个线程进入该方法,锁住的是线程对象
        if(tickets>0) {
            try {
                System.out.println(Thread.currentThread().getName() + "还剩" + tickets + "张票");
                tickets--;
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
        }
    }
}

这两个方法的不同之处在于,同步代码块锁代码,同步方法锁对象。

常见问题以及解决方案

但是这两个办法依然会造成一个问题,还是会造成线程一起进来一起出去,而造成异常数据。

我来给你翻译翻译什么是异常数据。

把任务想成是一箱水,但是这箱水是放在房间里的,进入房间的条件是只有箱子里有水我们才能进去,

但是拿水还是得一个一个的拿,如果现在还剩一瓶水,守门的大爷就是各种循环 if ,

大爷说 说还有一瓶水,赶紧进来,这时三个线程就进了房间,

但是第一个线程拿走了这瓶水,溜了,

第二个线程一看怎么没水了,但是他不管了,今天怎么都要拿一瓶的,他就拿了一瓶,

最后第三个线程在箱子前一看,好家伙,直接把水拿成-1瓶了,不管了,就把箱子里的水拿成了-2瓶。

知道了错误的原因,下面是解决的办法。

①只有一个A对象,那么就会把A对象锁住
意思就是把这个run方法中的具体任务方法单独放到一个类中,然后实例化这个类,调用。

上代码

class A{//创建一个类,将锁住的方法放到这个类中,这样我们再调用这个方法的时候就只会有一个对象。

    synchronized public void print()//这就是我说的把方法提出来🔒住
    {
        System.out.println(Thread.currentThread().getName()+":进入print方法");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":离开print方法");
    }

}

//这是实现runnable接口
class MythreadB implements  Runnable{
    private A myA;  //在这个接口里现在就只有一个A对象,这个段代码就是定义A类的名字叫myA

    public MythreadB(A a){//构造方法
        this.myA=a;
    }

    public void run(){
        this.myA.print();
    }
}
public class Synch{
    public static void main(String[] args) {
        MythreadB thread=new MythreadB(new A());  //线程使用同一个A对象
        new Thread(thread,"线程1").start();
        new Thread(thread,"线程2").start();
        new Thread(thread,"线程3").start();
    }
}

②全局锁:将Class对象锁住
锁住的类对象。
现在这个方法,直接把任务方法整成静态的 ,这样的话直接用类名就能调用了。
上代码


class A{
//定义为静态方法,也就是将A这个类锁住,锁就是MythreadB.class那么同一时刻,只能有一个类对象进入代码块   
    synchronized static  public void print(){
    
        System.out.println(Thread.currentThread().getName()+":进入print方法");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":离开print方法");
    }

}
class MythreadB implements  Runnable{

    public void run(){
       A.print();//现在我们调用的话就直接用类名调用就行了。
    }
}
public class Synch{
    public static void main(String[] args) {
        MythreadB thread=new MythreadB();
        new Thread(thread,"线程1").start();
        new Thread(thread,"线程2").start();
        new Thread(thread,"线程3").start();
    }
}

需注意:static同步方法只能绑定字节码类名.class。
谁调用就是谁的。谁调用就锁谁。无情铁锁。

这两个方法的不同之处在于,一个要实例化,然后在线程执行之前把这实例锁上,一个直接把任务方法的对象锁在了实现了runnable的类里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值