第四篇文章:Synchronized

采用互斥的方式,让同一时刻最多只有一个线程能持有这把锁,其他线程再想获取这把锁就会被阻塞住,这样就可以保证拥有锁的线程可以唯一执行临界区的代码


锁住Object类对象

Object object=new Object();

synchronized(object)
{


} 

 锁住普通方法

public  synchronized  void  buy()
{


}

锁住static方法:

public static synchronized  void  buy()
{


}

 Synchronized关键字使用的四种情景:

(1)锁住我们自己new出来的一个Object对象

public class test
{

  //lock是一个Object类型的对象
  Object lock=new Object();

  public void methodA()
  {
    //锁住Object对象lock,然后把代码放到这里面
    synchronized(lock)
    {
         pass;    
    }
  }

}

(2)锁住当前对象this

public class test


  public void methodA()
  {
    synchronized(this)
    {
         pass;    
    }
  }

}

下面两种加在方法前面的方式,本质还是锁住当前对象this,可以改成锁住this的等价方式

也就是说:synchronized只能锁对象,把synchronized加在方法(不管是静态方法还是非静态方法)前面,其实锁住的是this对象

(3)加在成员方法(也就是非static方法)前面

public class test
{
  public synchronized void methodA()
  {
    

  }

}


//等价于
public  class test
{

  public void methodA()
  {
     synchronized(this)
     {
         ..........
     }
  }
}

(4)加在static静态方法上

public class test
{
  public static synchronized void methodA()
  {
    

  }

}


//等价于
public class test
{
  public  static void  methodA()
  {
       synchronized(this)
       {
          ........
       }
  }
}

实际案例:

public class test
{
    static int counter=0;


    public static void main(String[] args) throws InterruptedException
    {
        Thread thread1=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                                counter++;
                                        }
                                        } );
        Thread thread2=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                                counter--;
                                        }
                                       } );
        thread1.start();
        thread2.start();
        Thread.sleep(10000);
        System.out.println(counter);


    }
}

方式一:将我们自己new出来的Object类对象作为锁

public class test
{
    static int counter=0;

    static  Object lock=new Object();


    public static void main(String[] args) throws InterruptedException
    {
        Thread thread1=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                            synchronized (lock)
                                            {
                                                counter++;
                                            }
                                        }
                                        } );
        Thread thread2=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                            synchronized(lock)
                                            {
                                                counter--;
                                            }
                                        }
                                       } );
        thread1.start();
        thread2.start();
        Thread.sleep(10000);
        System.out.println(counter);


    }
}

方式二:将当前对象this作为锁

class  Room
{
    private int counter=0;

    public  void add()
    {
        synchronized (this)
        {
            counter++;
        }
    }

    public void minus()
    {
        synchronized (this)
        {
            counter--;
        }
    }

    public int getCounter()
    {
        synchronized (this)
        {
            return counter;
        }
    }
}

public class test
{

    public static void main(String[] args) throws InterruptedException
    {
        Room  room=new Room();

        Thread thread1=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                            room.add();
                                        }
                                        } );
        Thread thread2=new Thread(()->{
                                        for(int i=0;i<5000;i++)
                                        {
                                            room.minus();
                                        }
                                       } );
        thread1.start();
        thread2.start();
        Thread.sleep(10000);
        System.out.println(room.getCounter());

    }
}

方式三:把synchronized加在非static方法前面

class  Room
{
    private int counter=0;

    public synchronized void add()
    {
            counter++;
    }

    public synchronized void minus()
    {
            counter--;
    }

    public synchronized int getCounter()
    {
            return counter;
    }
}

public class test
{

    public static void main(String[] args) throws InterruptedException
    {
        Room  room=new Room();

        Thread thread1=new Thread(()->{
            for(int i=0;i<5000;i++)
            {
                room.add();
            }
        } );
        Thread thread2=new Thread(()->{
            for(int i=0;i<5000;i++)
            {
                room.minus();
            }
        } );
        thread1.start();
        thread2.start();
        Thread.sleep(10000);
        System.out.println(room.getCounter());

    }
}

构造方法不能使用 synchronized 关键字修饰。

构造方法本身就属于线程安全的,不存在同步的构造方法一说

一个实例:三个线程来模拟三个窗口卖票


public class Test
{
    public static void main(String[] args) throws InterruptedException
    {
        Ticket ticket=new Ticket();
        Thread t1= new Thread(ticket,"t1");
        Thread t2=new Thread(ticket,"t2");
        Thread t3=new Thread(ticket,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
    static class Ticket implements Runnable
    {
        int ticket=100;

        @Override
        public void run()
        {
            while(true)
            {
                if(ticket>0)
                {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+" is selling ticket"+ticket--);
                }

            }
        }
    }
}

 出现了-1

方法一,new一个Object类对象,然后对这个对象上锁,synchronized锁住一个对象

public class Foo
{
    public static void main(String[] args) throws InterruptedException
    {
        Ticket ticket=new Ticket();
        Thread t1= new Thread(ticket,"t1");
        Thread t2=new Thread(ticket,"t2");
        Thread t3=new Thread(ticket,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
    static class Ticket implements Runnable
    {
        int ticket=100;
        Object object=new Object();

        @Override
        public void run()
        {
            synchronized(object)
            {
                while(true)
                {
                    if(ticket>0)
                    {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        System.out.println(Thread.currentThread().getName()+" is selling ticket"+ticket--);
                    }

                }
            }
        }
    }
}

 但这样只有1个线程可以卖票

方法二: synchronized锁住一个方法:

public class Test
{
    public static void main(String[] args) throws InterruptedException
    {
        Ticket ticket=new Ticket();
        Thread t1= new Thread(ticket,"t1");
        Thread t2=new Thread(ticket,"t2");
        Thread t3=new Thread(ticket,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
    static class Ticket implements Runnable
    {
        int ticket=100;
        
        @Override
        public void run()
        {
            buy();
        }

        public synchronized void buy()
        {
            while(true)
            {
                if(ticket>0)
                {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+" is selling ticket"+ticket--);
                }

            }
        }
    }
}

 这样也只有1个线程可以卖票

方法三:使用Reentrantlock进行加锁

public class Test
{
    public static void main(String[] args) throws InterruptedException
    {
        Ticket ticket=new Ticket();
        Thread t1= new Thread(ticket,"t1");
        Thread t2=new Thread(ticket,"t2");
        Thread t3=new Thread(ticket,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
    static class Ticket implements Runnable
    {
        int ticket=100;
        Reentrantlock lock=new Reentrantlock();

        @Override
        public void run()
        {
            while(true)
            {
                lock.lock()
            
                if(ticket>0)
                {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+" is selling ticket"+ticket--);
                }
                
                lock.unlock();
            }
        }
    }
}

sychronized的两个特性:可重入性和不可中断性

可重入性:

可以重复拿到一把锁(一把锁拿两次)

Object  obj=new Obj();


synchronized(obj)
{

   synchronized(obj)
   {
      .......
   }
}

可重入特性可以避免死锁

如果没有可重入性,第一次拿到锁之后就会被阻塞住,无法第二次拿到锁

被锁住的对象内部有一个计数器记录线程获取几次锁了

synchronized经常会和单例模式(加锁双重校验懒汉模式)结合起来考察

synchronized原理

   synchronized(obj)
   {
      .......
   }

 被锁住的Object类对象会关联(引用)一个monitor对象(monitor对象也会引用这个Object类对象),这个才是真正的锁,不是java对象,而是一个c++对象,这个对象不是我们主动创建的,而是由jvm创建的

这个monitor对象里面有两个重要的成员变量:owner  拥有锁的线程,recursions  记录一个线程拿了几次锁(可重入的话,recursion可能为2)

假设t1线程来执行,那monitor对象的owner就会变成t1

如果此时t2线程来,发现monitor对象的owner是t1,不是自己,那t2就会进入阻塞状态

多个线程竞争锁,没有竞争到锁的线程会进入EntryList阻塞队列,进入blocked状态,拿到锁的线程调用wait()方法后,进入WaitSet等待队列,进入waiting等待状态,有其他线程调用notify()方法唤醒处于waiting的线程,被唤醒的线程就会进入EntryList阻塞队列进入阻塞状态共同竞争锁

sunchronized锁升级:

 jdk1.6之前,synchronized使用的就是重量级锁

1.6之后就有个锁升级(也叫锁膨胀)的过程:

(1)任何对象都能充当锁,你刚把对象创建出来的时候,对象处于无锁的状态

(2)当有一个线程(只有一个线程)过来想要获取锁的时候,这个充当锁的对象就要升级成偏向锁

偏向锁认为:大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得这把锁,当这个线程再次请求锁的时候,直接将锁给它

(3)当有多个线程竞争锁的时候,升级成轻量级锁(也叫自旋锁),没拿到锁的线程进行自旋(忙循环),自旋完再尝试去拿锁,如果此时发现所还是被占用不可获得,继续自旋

没有抢到锁的线程不会进入到阻塞态(因为线程切换到阻塞态的开销很大,所以尽量避免进入阻塞状态),自旋锁认为锁只会被拿走很短的时间,过了这个很短的时间拿到锁,访问完共享数据的线程就会归还锁

(4)自旋超过阈值(默认是10次),就会升级成重量级锁

面试题:synchronized  异常会释放锁吗?会,会执行monitorexit释放锁对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值