线程基础-synchronized概念和应用

线程基础-synchronized概念和应用

Java的锁synchronized

在《java编程思想》一书中,有这样一个例子:在浴室里多个人都希望能单独使用浴室(即共享资源)。这时使用浴室,一个人先敲门,看看是否能使用。如果没人的话,他就进入浴室并锁上门。这时其他人要使用浴室的话,就会被“阻挡”,所以他们要在浴室的门口等待,直到浴室可以使用。

当浴室使用完毕,就该把浴室给其他人使用了,这个比喻就有点不太确定了,事实上,人们并没有排队,我们也不能确定谁将是下一个使用浴室的人,因为线程的调度机制是不确定性的。实际情况是:等待浴室的人们簇拥在浴室的门口,当锁住浴室门的那个人打开锁准备离开的时候,离门最近的那个人可能进入浴室。

如前所述,可以通过yield()setPriority()来给线程调度器提供建议,但这些建议未必会有多大的效果,这取决你的具体平台和jvm的实现。

Java提供了关键字synchronized,为防止资源冲突提供了内置的支持。synchronized是一种同步锁。他修饰的对象有以下几种:

1)修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 

3)修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; 

4)修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

 

修饰一个代码块

1/一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

如图:

 

注意:上面的例子,thread1thread2同时执行,是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联。在使用并发时,将域设置为private是非常重要的,否则,synchronized关键字就不能防止其它任务直接访问域,这样会产生冲突。

 

多个线程多个锁

 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(lock)。两个对象,线程获得的就是两个不同的锁,他们互不影响。有一种情况则是相同的锁,即在静态方法上加synchronized关键字,表示锁定。class类,类一级别的锁(独占。class类)


修饰一个方法

synchronized修饰一个方法很简单,就是在方法的前面加synchronizedpublic synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个方法。

如图:

 

 注意:

1/虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

2/在定义接口方法时不能使用synchronized关键字。

3/构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

 

 

修饰一个静态方法

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

代码如下:

public class MyThread implements Runnable{

 

private static int count;

public synchronized static void method() {

  

}

public MyThread() {

count=0;

}

   public synchronized static void method1() {

      for (int i = 0; i < 5; i ++) {

         try {

            System.out.println(Thread.currentThread().getName() + ":" + (count++));

            Thread.sleep(100);

         } catch (InterruptedException e) {

            e.printStackTrace();

         }

      }

   }

   

 public int getCount() {

      return count;

   }

 

 

 

 public static void main(String[] args) {

 MyThread syncThread = new MyThread();

 Thread thread1 = new Thread(syncThread, "syncThread1");

 Thread thread2 = new Thread(syncThread, "syncThread2");

 thread1.start();

 thread2.start();

}

 

@Override

public synchronized  void run() {

method1();

}

 

}

syncThread1syncThread2SyncThread的两个对象,但在thread1thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1syncThread2相当于用了同一把锁。

注意:一个任务可以多次获取对象的锁。如果一个方法在同一个对象上调用了第二个方法,后者又调用了同一对象上的另一个方法,就会发生这种情况。Jvm负责跟踪对象加锁的次数。如果一个对象被解锁,(即完全释放锁)其计数变为0。在任务第一次给对象加锁的时候,计数变为1.每当这个相同的任务在这个对象上获取锁时,计数都会递增。显然,只有首先获得了锁的任务才能允许继续获取多个锁。每当任务离开一个synchronized方法,计数递减,当计数为零的时候,锁内完全释放,此时别的任务就可以使用此资源了。

 

 

修饰一个类

代码如下:

public class MyThread implements Runnable{

 

private static int count;

public synchronized static void method() {

  

}

public MyThread() {

count=0;

}

 public static void method1() {

      synchronized(MyThread.class) {

         for (int i = 0; i < 5; i ++) {

            try {

               System.out.println(Thread.currentThread().getName() + ":" + (count++));

               Thread.sleep(100);

            } catch (InterruptedException e) {

               e.printStackTrace();

            }

         }

      }

   }

   

 public int getCount() {

      return count;

   }

 

 

 public static void main(String[] args) {

 MyThread syncThread = new MyThread();

 Thread thread1 = new Thread(syncThread, "SyncThread1");

 Thread thread2 = new Thread(syncThread, "SyncThread2");

 thread1.start();

 thread2.start();

}

 

@Override

public synchronized  void run() {

method1();

}

 

}

Synchronized作用于一个类时,是给这个类加锁,他的所有对象用的是同一把锁。

注意在什么时候同步呢?可以运用Brian的同步规则:

如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。

 

总结:

1/每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 

2/无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 (注意:(1)不要使用String常量加锁,会出现死循环问题.(2)锁对象的改变问题,当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那么持有的所就不同,如果对象本身不发生改变,那么依然就是同步的,即使对象的属性发生了改变。)

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值