Java并发编程系列之Lock与synchronized

news/2024/5/17 15:48:26 标签: Lock, synchronized

Locksynchronized__0">Locksynchronized 的区别

ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的
集群部署、分布式部署下,synchronized 不是线程安全的,因为属于不同的JVM

1、lock是可中断锁,支持中断处理,而synchronized 不是可中断锁

线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定的三种方式:

a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁

b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

c) tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

d) lockInterruptibly():如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

2、synchronized是在JVM层面上实现的,lock是通过代码实现的,JVM会自动释放锁定(代码执行完成或者出现异常),但是使用Lock则不行,要保证锁定一定会被释放,就必须将unLock()放到finally{}中。

3、在资源竞争不是很激烈的情况下,synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReentrantLock的性能能维持常态;

4、与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。

5、ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合(以后会阐述Condition)。

6、ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。

7、ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。

synchronized_36">synchronized用法

synchronized修饰的对象有几种

  • 修饰一个类:其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象;

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

  • 修饰一个静态的方法:其作用的范围是整个方法,作用的对象是这个类的所有对象;

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

修饰代码块

作用于当前对象实例的代码块

public class PoolUpdater implements Observer {
    /**
     * Create a ScheduledExecutorService to remove unused DataSources.
     */
    public void init() {
        if (inited) {
            return;
        }
        // this 为当前对象实例
        synchronized (this) {
            if (inited) {
                return;
            }
            if (intervalSeconds < 10) {
                LOG.warn("CAUTION: Purge interval has been set to " + intervalSeconds
                        + ". This value should NOT be too small.");
            }
            if (intervalSeconds <= 0) {
                intervalSeconds = DEFAULT_INTERVAL;
            }
            executor = Executors.newScheduledThreadPool(1);
            executor.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    LOG.debug("Purging the DataSource Pool every " + intervalSeconds + "s.");
                    try {
                        removeDataSources();
                    } catch (Exception e) {
                        LOG.error("Exception occurred while removing DataSources.", e);
                    }
                }
            }, intervalSeconds, intervalSeconds, TimeUnit.SECONDS);
        }
    }
}

@SuppressWarnings("unchecked")
static Map<Object, ObjectName> getInstances0() {
    Properties properties = System.getProperties();
    Map<Object, ObjectName> instances = (Map<Object, ObjectName>) properties.get(SYS_PROP_INSTANCES);

    if (instances == null) {
    	// 共享变量 properties
        synchronized (properties) {
            instances = (IdentityHashMap<Object, ObjectName>) properties.get(SYS_PROP_INSTANCES);

            if (instances == null) {
                instances = Collections.synchronizedMap(new IdentityHashMap<Object, ObjectName>());
                properties.put(SYS_PROP_INSTANCES, instances);
            }
        }
    }

    return instances;
    }

修饰静态方法和类

给当前类加锁,会作用于类所有对象实例的代码块

public synchronized static ObjectName addDataSource(Object dataSource, String name) {
    final Map<Object, ObjectName> instances = getInstances();

    MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
    synchronized (instances) {
        if (instances.size() == 0) {
            try {
                ObjectName objectName = new ObjectName(MBEAN_NAME);
                if (!mbeanServer.isRegistered(objectName)) {
                    mbeanServer.registerMBean(instance, objectName);
                }
            } catch (JMException ex) {
                LOG.error("register mbean error", ex);
            }

            DruidStatService.registerMBean();
        }
    }

    ObjectName objectName = null;
    if (name != null) {
        try {
            objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + name);
            mbeanServer.registerMBean(dataSource, objectName);
        } catch (Throwable ex) {
            LOG.error("register mbean error", ex);
            objectName = null;
        }
    }

    if (objectName == null) {
        try {
            int id = System.identityHashCode(dataSource);
            objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + id);
            mbeanServer.registerMBean(dataSource, objectName);
        } catch (Throwable ex) {
            LOG.error("register mbean error", ex);
            objectName = null;
        }
    }

    instances.put(dataSource, objectName);
    return objectName;
}

public abstract class ElasticSearchConfigBoot {
	private static boolean inited = false;
	public static void boot(){
		boot(false);
	}

	/**
	 *
	 * @param forceBoot 强制启动
	 */
	public static void boot(boolean forceBoot){
		if(inited) {
			if(!forceBoot)
				return;
		}
		// 修饰一个类
		synchronized (ElasticSearchConfigBoot.class) {
			if(inited){
				if(!forceBoot)
					return;
			}
			try {
				final BaseApplicationContext context = DefaultApplicationContext.getApplicationContext("conf/elasticsearch-boot-config.xml",forceBoot);
				String _elasticsearchServerNames = context.getExternalProperty("elasticsearch.serverNames", "default");
				String[] elasticsearchServerNames = _elasticsearchServerNames.split(",");

				//初始化Http连接池
				GetProperties getProperties = new WrapperGetProperties(context);
				ClientConfiguration.bootClientConfiguations(elasticsearchServerNames, getProperties );
				ClientConfiguration.bootHealthCheckClientConfiguations(elasticsearchServerNames,getProperties);
				//初始化ElasticSearchServer
				ElasticSearchHelper.booter(elasticsearchServerNames, context,forceBoot,false);
			}
			finally {
				inited = true;
			}
		}

	}
}

修饰普通方法

@Override
public synchronized boolean next() throws SQLException {
    if (closed) {
        throw new SQLException();
    }

    if (rowIndex < rows.size() - 1) {
        rowIndex++;
        return true;
    }
    return false;
}

Lock_221">Lock的使用

Jdk1.5中,在java.util.concurrent.locks包下,有一组实现线程同步的接口和类,说到线程的同步,很多立马就会想到synchronized关键字,这是java内置的关键字,用来处理线程同步的,但这个关键字有很多的缺陷,使用起来也不是很方便和直观,所以就出现了Lock,下面,我们就来对比着讲解Lock

synchronized关键字在使用的过程中会有如下几个问题:

1、不可控性,无法做到随心的加锁和释放锁

2、效率比较低下,比如我们现在并发的读两个文件,读与读之间是互不影响的,但如果给这个读的对象使用synchronized来实现同步的话,那么只要有一个线程进入了,那么其他的线程都要等待。

3、无法知道线程是否获取到了锁

以上问题,Lock都可以很好的解决,并且jdk1.5还提供了各种锁,例如读写锁,但有一点需要注意,使用synchronized关键时,无须手动释放锁,但使用Lock必须手动释放锁。下面我们就来学习一下Lock锁。

java.util.concurrent.locks.Lock是一个上层的接口,其原型如下,总共提供了6个方法:

public interface Lock {
	// 用来获取锁,如果锁已经被其他线程获取,则一直等待,直到获取到锁
	void lock();

	// 该方法获取锁时,可以响应中断,比如现在有两个线程,一个已经获取到了锁,另一个线程调用这个方法正在等待锁,
	// 但是此刻又不想让这个线程一直在这死等,可以通过调用线程的Thread.interrupted()方法,来中断线程的等待过程
	void lockInterruptibly() throws InterruptedException;

	// tryLock方法会返回bool值,该方法会尝试着获取锁,如果获取到锁,就返回true,如果没有获取到锁,就返回false,但是该方法会立刻返回,而不会一直等待
	boolean tryLock();

	// 这个方法和上面的tryLock差不多是一样的,只是会尝试指定的时间,如果在指定的时间内拿到了锁,则会返回true,如果在指定的时间内没有拿到锁,则会返回false
	boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

	// 释放锁
	void unlock();

	// 实现线程通信,相当于wait和notify,后面会单独讲解
	Condition newCondition();
}

那么这几个方法该如何使用了?前面我们说到,使用Lock是需要手动释放锁的,但是如果程序中抛出了异常,那么就无法做到释放锁,有可能引起死锁,所以我们在使用Lock的时候,有一种固定的格式,如下:

// 下面这段代码摘自官方文档  
Lock l = ...;  
l.lock();  
try {  
    // access the resource protected by this lock  
} finally {// 必须使用try,最后在finally里面释放锁  
    l.unlock();  
}  

下面我们来看一个简单的例子,代码如下:

/**
 * 描述:Lock使用
 */
public class LockDemo {
	// new一个锁对象,注意此处必须声明成类对象,保持只有一把锁,ReentrantLockLock的唯一实现类
	Lock lock = new ReentrantLock();

	public void readFile(String fileMessage) {
		lock.lock();// 上锁
		try {
			System.out.println(Thread.currentThread().getName() + "得到了锁,正在读取文件……");
			for (int i = 0; i < fileMessage.length(); i++) {
				System.out.print(fileMessage.charAt(i));
			}
			System.out.println();
			System.out.println("文件读取完毕!");
		} finally {
			System.out.println(Thread.currentThread().getName() + "释放了锁!");
			lock.unlock();
		}
	}

	public void demo(final String fileMessage) {
		// 创建若干个线程
		ExecutorService service = Executors.newCachedThreadPool();
		// 提交20个任务
		for (int i = 0; i < 20; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					readFile(fileMessage);
					try {
						Thread.sleep(20);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
		// 释放线程池中的线程
		service.shutdown();
	}
	
	public static void main(String[] args) {
		LockDemo demo = new LockDemo();
		demo.demo("Hello World");
	}
}
  • 我们先把锁的那两行代码注释掉,看下效果如何:
    这里写图片描述

  • 然后我们把锁的代码加上,看下效果如何:
    这里写图片描述

如果我们把上面的readFile方法前面加上synchronized关键字,然后把锁去掉,效果是一样的。

tryLock锁的使用和Lock锁的使用类似,下面把示例代码贴出来,就不做过多的说明了,代码如下:

/**
 * 描述:TryLock使用
 */
public class TryLockDemo {
	// new一个锁对象
	Lock lock = new ReentrantLock();

	public void readFile(String fileMessage) {
		if (lock.tryLock()) {
			try {
				System.out.println(Thread.currentThread().getName() + "得到了锁,正在读取文件……");
				System.out.println("文件读取完毕!");
			} finally {
				System.out.println(Thread.currentThread().getName() + "释放了锁!");
				lock.unlock();
			}
		} else {
			System.out.println(Thread.currentThread().getName() + "获取锁失败!");
		}
	}

	public void demo(final String fileMessage) {
		// 创建若干个线程
		ExecutorService service = Executors.newCachedThreadPool();
		// 提交200个任务
		for (int i = 0; i < 200; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					readFile(fileMessage);
				}
			});
		}
		// 释放线程池中的线程
		service.shutdown();
	}

	public static void main(String[] args) {
		TryLockDemo demo = new TryLockDemo();
		demo.demo("Hello World");
	}
}
  • 测试效果如下:
    这里写图片描述

参考文档:

  1. https://blog.csdn.net/fighterandknight/article/details/54906507
  2. https://blog.csdn.net/fighterandknight/article/details/54945559

http://www.niftyadmin.cn/n/1364649.html

相关文章

Pytorch学习之Cifer实战四·激活函数+损失函数+优化器详解

最后插个小彩蛋&#xff0c;还是转了陶将博主的一张图&#xff1a; 下图是各个算法在等高线的表现&#xff0c;它们都从相同的点出发&#xff0c;走不同的路线达到最小值点。可以看到&#xff0c;Adagrad&#xff0c;Adadelta和RMSprop在正确的方向上很快地转移方向&#xff0…

C# 光标文件的创建

base.m_cursor new System.Windows.Forms.Cursor(GetType(), "Resources.MeasuredisTool.cur"); Resources文件夹的位置一定要与GetType对应的类的命名空间一致&#xff1b; 如&#xff1a; 转载于:https://www.cnblogs.com/ccjcjc/p/3418583.html

可重入锁-ReentrantLock

ReentrantLock&#xff0c;可重入锁&#xff0c;是一种递归无阻塞的同步机制。它可以等同于synchronized的使用&#xff0c;但是ReentrantLock提供了比synchronized更强大、灵活的锁机制&#xff0c;可以减少死锁发生的概率。 “可重入锁”概念是&#xff1a;自己可以再次获取…

【cocos2d-x 手游研发小技巧(2)循环无限滚动的登陆背景】

原创文章&#xff0c;转载请附上链接&#xff1a;http://www.cnblogs.com/zisou/p/cocos2d-xARPG6.html 首先让大家知道我们想要实现的最终效果是什么样的&#xff1f; 看一个《逆天仙魔录》例子图&#xff1a; 就是一个连贯循环的背景图&#xff0c;如何让他无间隔的循环连贯跑…

负载均衡【Load Balance】

介绍 负载均衡 建立在现有网络结构之上&#xff0c;它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 负载均衡&#xff0c;英文名称为Load Balance&#xff0c;其意思就是分摊到多个操作单元上进行…

CNN经典之LeNet网络+PyTorch复现

一、前情说明&#xff1a; 写在前面的话 本系列博客在于汇总CSDN的精华帖&#xff0c;类似自用笔记&#xff0c;不做学习交流&#xff0c;方便以后的复习回顾&#xff0c;博文中的引用都注明出处&#xff0c;并点赞收藏原博主。 本博客大致分为两部分&#xff0c;第一部是转…