2.java多线程_synchronized(Lock)同步代码块

1.synchronized同步关键词

  线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同

操作共享数据。因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,

其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线

程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,

只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,

synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能)

1.1.synchronized是Java中的关键字,是一种同步锁,它修饰的对象有以下几种:

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

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

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

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

1.2.synchronized同步代码块

 

  同步格式:
        synchronized( 需要一个任意的对象(锁) ){
            代码块中放操作共享数据的代码。
        }

代码示例:

package cn.thread;
public class MySynchronized {
    public static void main(String[] args) {
        final MySynchronized mySynchronized = new MySynchronized();
        final MySynchronized mySynchronized2 = new MySynchronized();
        new Thread("thread1") {
            public void run() {
                synchronized (mySynchronized) {
                try {
                    System.out.println(this.getName()+" start");
                    //int i =1/0;   //如果发生异常,jvm会将锁释放
                    Thread.sleep(5000);
                    System.out.println(this.getName()+"醒了");
                    System.out.println(this.getName()+" end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                }
            }
        }.start();
        new Thread("thread2") {
            public void run() {
                synchronized (mySynchronized) {        //争抢同一把锁时,线程1没释放之前,线程2只能等待
//synchronized (mySynchronized2) {    //如果不是一把锁,可以看到两句话同时打印,各自运行,相互不影响
                    System.out.println(this.getName()+" start");
                    System.out.println(this.getName()+" end");
                }
            }
        }.start();
    }
}

1.3.synchronized缺陷

  如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而

这里获取锁的线程释放锁只会有两种情况:

  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有(自动释放)

  2)线程执行发生异常,此时JVM会让线程自动释放锁

例子1:
  如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,
这多么影响程序执行效率。   因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。 例子2:   当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。   但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等
待无法进行读操作。   因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。   另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。   总的来说,也就是说Lock提供了比synchronized更多的功能。

 

2.Lock同步

 

2.1.Lock和synchronized的区别

  1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问

  2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统

会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象

  3)Lock是一个接口,在java.util.concurrent.locks包下

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
}

Lock接口中每个方法的使用:lock()tryLock()tryLock(long time, TimeUnit unit)lockInterruptibly()是用来获取锁的

unLock()方法用来释放锁
 

 

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

猜你喜欢

转载自www.cnblogs.com/yaboya/p/9153851.html