原先多线程并发编程的学习笔记和代码整理一下贴上来。
---------------------------------
synchronized与Lock
Java中提供了2种线程同步的方式,一种是Java语言级的同步原语synchronized关键字,另一种是使用javase5中新提供的java.util.concurrent.locks.Lock相关接口。
一、synchronized
关于synchronized,大家应该都比较熟悉了,从最早的java规范开始就包含了这个关键字。
1、synchronized作用域:
(1)是某个对象实例范围。synchronized防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。但不同对象实例的synchronized方法是不相干扰的,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。
(2)是某个类的范围。防止多个线程同时访问这个类中的synchronized static方法,它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;如果在另一个对象上同步synchronized(other){/*区块*/},就要确保所有相关线程都在同一个对象上同步。
当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){}。在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
二、Lock相关接口
对于Lock接口,JDK文档描述如下:
Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。
Lock接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁,从而支持使用这种技术。
Lock对象必须被显示的创建、锁定和释放,这是与synchronized的区别之一。
1、Lock接口与ReentrantLock:
使用ReentrantLock所需代码比synchronized要多,通常为解决一些特殊问题,如:
synchronized关键字不能尝试着获取锁且最终获取锁会失败,或者尝试着获取锁然后放弃它。
ReentrantLock允许你尝试获取锁并但最终并未获取,synchronized若获取不到锁则会一直阻塞。
举例:首先定义一个task,使用ReentrantLock作为锁。
class LockTask{ private Lock lock=new ReentrantLock(); public Lock getLock(){ return lock; } public void lockA(){ boolean locked=lock.tryLock(); try{ System.out.println("isLocked:"+locked); }finally{ if(locked){ lock.unlock(); } } } public void lockB(){ boolean locked=false; try { locked=lock.tryLock(5, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } try{ System.out.println("isLocked(5s)="+locked); }finally{ if(locked){ lock.unlock(); } } } }
注意lockA和lockB中的tryLock方法,仅在调用时锁未被另一个线程保持的情况下,才获取该锁。如果锁被另一个线程保持,则此方法将立即返回false。 其中lockB设置了tryLock的时间为5s。注意一定要在finally中释放锁。
下面测试时,会打开一个线程获取先lock住锁,之后再调用lockA和B方法。其中Thread.currentThread().join(1000);保证lock操作一定会执行完。
final LockTask task=new LockTask(); task.lockA(); task.lockB(); new Thread(){ {setDaemon(true);} public void run(){ task.getLock().lock(); System.out.println("lock!"); } }.start(); Thread.currentThread().join(1000); task.lockA(); task.lockB();查看输出:
isLocked:true isLocked(5s)=true lock! isLocked:false isLocked(5s)=false可见当已经有线程拿到锁时,再次尝试获取锁会失败(返回false),而synchronized则会一直阻塞等待。
2、ReadWriteLock接口与ReentrantReadWriteLock
java.util.concurrent.locks包中另一个接口是ReadWriteLock读写锁。
关于ReadWriteLock,JDK文档中描述:
ReadWriteLock维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个reader线程同时保持。写入锁是独占的。
意思就是没有写锁时,读锁可以由多个线程保持,而有写锁只能由一个线程持有。
举例:
定义一个类包含2个方法,read和write。read方法获取读锁,write方法获取写锁。
class ReadWrite{ int data = 1; ReadWriteLock rwl = new ReentrantReadWriteLock(); public void read(){ rwl.readLock().lock(); try{ System.out.println(Thread.currentThread().getName()+" reading..."); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" read:" + data); }catch(InterruptedException e){ e.printStackTrace(); }finally{ rwl.readLock().unlock(); } } public void write(int data){ rwl.writeLock().lock(); try{ System.out.println(Thread.currentThread().getName()+" writing..."); this.data = data; Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" written:" + data); }catch(InterruptedException e){ e.printStackTrace(); }finally{ rwl.writeLock().unlock(); Thread.yield();//让步 } } }
上面程序中,让read方法持有锁1s,且只进行读操作打印data值,write方法持有锁的时间稍短一些0.1s,同时进行写操作给data赋值。
进行读写操作:
此处启动3个线程,一个循环写入data随机数,另外2个线程循环读取data值。
final ReadWrite readWrite = new ReadWrite(); new Thread(){ public void run(){ while(true){ readWrite.write(new Random().nextInt(1000)); } } }.start(); Thread.sleep(1000); for(int i=0;i<2;i++){ new Thread(){ public void run() { while(true){ readWrite.read(); } } }.start(); }程序运行后一段时间,可以看到,写数据的时候不能读,读数据的时候不能写,但可以同时2个线程在读。
所以说,写锁是独占的,读锁可以由多个线程保持。
另外,JDK文档中提到另外一个示例:
在使用某些种类的 Collection时,可以使用ReentrantReadWriteLock来提高并发性。通常,在预期 collection很大,读取者线程访问它的次数多于写入者线程,并且entail操作的开销高于同步开销时,这很值得一试。以下是一个使用 TreeMap的类,预期它很大,并且能被同时访问。
class RWDictionary { private final Map<String, Data> m = new TreeMap<String, Data>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } public Data put(String key, Data value) { w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } public void clear() { w.lock(); try { m.clear(); } finally { w.unlock(); } } }使用读锁get数据和获取key,使用写锁put数据和清除map。注意要unlock。
相关推荐
│ 高并发编程第一阶段18讲、数据同步的引入与Synchronized的简单介绍.mp4 │ 高并发编程第一阶段19讲、结合jconsole,jstack以及汇编指令认识synchronized关键字.mp4 │ 高并发编程第一阶段20讲、同步代码块以及...
│ 高并发编程第一阶段18讲、数据同步的引入与Synchronized的简单介绍.mp4 │ 高并发编程第一阶段19讲、结合jconsole,jstack以及汇编指令认识synchronized关键字.mp4 │ 高并发编程第一阶段20讲、同步代码块以及...
《Java并发编程实践》是Java开发者必读的经典之作,由Brian Goetz等多位专家共同撰写。这本书深入浅出地探讨了Java平台上的并发问题,帮助读者理解和掌握如何编写高效、可靠且可维护的多线程应用程序。以下是该书...
### Java分布式应用学习笔记06浅谈并发加锁机制分析 #### 1. 前言 在深入探讨Java中的并发加锁机制之前,我们有必要回顾一下多线程环境下的一些基本...希望本文能帮助读者更好地理解和掌握Java并发编程的相关知识。
- **书籍推荐**:《Effective Java》、《Java并发编程实战》等经典著作。 - **在线课程**:Coursera、edX等平台上有很多优质的Java编程课程。 - **社区与论坛**:Stack Overflow、GitHub等社区可以帮助解决实际问题...
### Java并发编程 #### 1. 多线程基础 - **线程的概念**:进程中的执行单元。 - **线程的创建**:继承Thread类或实现Runnable接口。 - **线程的生命周期**:新建、就绪、运行、阻塞、死亡等状态。 #### 2. 线程...
5. **多线程**:Java中的并发编程是重要的面试话题,包括线程的创建方式(实现Runnable接口或继承Thread类)、同步机制(synchronized关键字、Lock接口)、并发工具类(如Semaphore、CountDownLatch、CyclicBarrier...
3. **并发编程**:Java提供了丰富的并发工具,如线程、线程池、synchronized、volatile、Lock接口、并发容器(如ConcurrentHashMap)。面试中常考察对并发模型的理解和死锁、活锁、饥饿等问题的解决策略。 4. **...
Java提供了两种主要的同步方式:`synchronized`关键字和`java.util.concurrent`包中的高级并发工具。`synchronized`提供了简单的同步,适用于大多数情况,但有时可能会导致死锁和竞态条件。高级并发工具,如`...
6. **线程与并发**:理解Java中的多线程编程,如Thread类、Runnable接口,同步机制(synchronized关键字、Lock接口),以及并发工具类如Semaphore、CyclicBarrier等。 7. **反射与注解**:反射机制允许在运行时动态...
- **线程同步**:synchronized关键字和Lock接口的使用,避免并发访问时的数据不一致性问题。 - **线程池**:Executor框架的使用,提高线程管理的效率。 #### 5. 泛型与反射 - **泛型**:深入理解泛型的工作原理,...
多线程编程是Java的一大特色,课程会介绍Thread类的使用,同步机制(synchronized关键字和Lock接口)以及并发工具类。 **Day16-17: Lambda表达式与函数式编程** 讲解Java 8引入的Lambda表达式,以及Stream API,使...
- **并发编程**:包括线程的概念、线程同步机制(synchronized关键字、Lock接口)、线程间通信、并发工具类(Executor框架等)等内容。 - **反射与注解**:解释反射机制的工作原理,以及如何利用注解来增强代码的...
微课会介绍Thread类,如何创建和控制线程,以及同步机制如synchronized关键字和Lock接口。 7. **微课设计原则**:在设计微课时,应遵循教学设计的科学性,包括目标明确、内容精炼、互动性强、反馈及时等原则。微课...
第二课 - 线程.ppt: 线程是Java并发编程的核心,讲解了线程的创建、状态管理、同步机制(如synchronized、wait/notify、Lock接口)、线程池等。理解线程的使用和管理对于提升程序的运行效率和避免并发问题至关重要。...
理解线程的生命周期、同步机制(synchronized、volatile、Lock)以及并发工具类(如Semaphore、CountDownLatch、CyclicBarrier)是必不可少的。 5. **异常处理**:Java的异常处理机制通过try-catch-finally语句块...
4. **多线程**:线程的创建、同步机制(synchronized、Lock)、并发工具类。 5. **IO流**:字节流、字符流、缓冲流、对象序列化。 6. **网络编程**:Socket通信、HTTP协议。 7. **JVM内存模型**:堆、栈、方法区、...
6. **多线程编程**:Java提供了丰富的多线程支持,包括Thread类、Runnable接口以及同步机制(synchronized关键字、Lock接口等),这些知识对于构建并发应用程序很有帮助。 7. **数据库连接与JDBC**:学习如何使用...
- **并发编程**:线程安全、同步机制(synchronized、volatile、Lock等)、线程池的理解与应用。 - **内存管理**:垃圾回收机制、内存泄漏、内存溢出等问题。 - **性能优化**:CPU、内存、磁盘I/O等方面的优化...