使用锁实现同步

  Java提供了同步代码块的另一种机制,它是一种比synchronized关键字更强大也更灵活的机制。这种机制基于Lock接口及其实现类(例如ReentrantLock),提供了更多的好处。

  • 支持更灵活的同步代码块结构。使用synchronized关键字时,只能在同一个syanchronized块结构中获取和释放控制。Lock接口允许实现更复杂的临界区结构(即控制的获取和释放不出现在同一个块结构中)。
  • 相比synchronized关键字,Lock接口提供了更多的功能。其中一个新的功能是tryLock()方法的实现。这个方法试图获取锁,如果锁已经被其他线程获取,它将返回false并继续往下执行代码。使用synchronized关键字时,如果线程A试图执行一个同步代码块,而线程B已在执行这个同步代码块,则线程A就会被挂起直到线程B运行完这个同步代码块。使用锁的tryLock()方法,通过返回值将得知是否有其他线程正在使用这个锁保护的代码块。
  • Lock接口允许分离读和写操作,允许多个读线程和只有一个写线程。
  • 相比synchronized关键字,Lock接口具有更好的性能。

  下面我们将学习如何使用锁来同步代码,并且使用Lock接口和它的实现类——ReentrantLock类来创建一个临界区。这个范例将模拟打印队列。

1. 创建一个打印队列类PrintQueue。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintQueue {
    //声明一个锁对象,并且用ReentrantLock类初始化
    private final Lock queueLock = new ReentrantLock();
    //实现打印方法
    public void printJob(Object doucument){
        queueLock.lock();
        Long duration = (long) (Math.random()*10000);
        System.out.println(Thread.currentThread().getName()+": PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
        try {
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
    }

}

2. 创建打印工作类Job并且实现Runnable接口。

public class Job implements Runnable {
    private PrintQueue printQueue;
    public Job(PrintQueue printQueue){
        this.printQueue = printQueue;
    }
    @Override
    public void run() {
        System.out.printf("%s: Going to print a document\n", Thread.currentThread().getName());
        printQueue.printJob(new Object());
        System.out.printf("%s: The Document has been printed\n", Thread.currentThread().getName());
    }

}

3. 创建范例的主类Main

public class Main {

    public static void main(String[] args) {
        //创建一个共享的打印队列对象
        PrintQueue printQueue = new PrintQueue();
        //创建10个打印工作Job对象
        Thread threads[] = new Thread[10];
        for(int i=0;i<10;i++){
            threads[i] = new Thread(new Job(printQueue), "Thread"+i);
        }
        //启动10个线程
        for(int i=0;i<10;i++){
            threads[i].start();
        }
    }
}

4. 程序执行结果如下所示

Thread0: Going to print a document
Thread8: Going to print a document
Thread6: Going to print a document
Thread8: PrintQueue: Printing a Job during 5 seconds
Thread4: Going to print a document
Thread2: Going to print a document
Thread9: Going to print a document
Thread7: Going to print a document
Thread5: Going to print a document
Thread1: Going to print a document
Thread3: Going to print a document
Thread8: The Document has been printed
Thread6: PrintQueue: Printing a Job during 2 seconds
Thread6: The Document has been printed
Thread4: PrintQueue: Printing a Job during 4 seconds
Thread4: The Document has been printed
Thread0: PrintQueue: Printing a Job during 1 seconds
Thread0: The Document has been printed
Thread2: PrintQueue: Printing a Job during 6 seconds
Thread2: The Document has been printed
Thread9: PrintQueue: Printing a Job during 6 seconds
Thread9: The Document has been printed
Thread7: PrintQueue: Printing a Job during 1 seconds
Thread5: PrintQueue: Printing a Job during 5 seconds
Thread7: The Document has been printed
Thread5: The Document has been printed
Thread1: PrintQueue: Printing a Job during 2 seconds
Thread1: The Document has been printed
Thread3: PrintQueue: Printing a Job during 0 seconds
Thread3: The Document has been printed

  这个范例中,在printJob()这个临界区的开始,必须通过lock()方法获得对锁的控制。当线程A访问这个方法时,如果没有其他线程获取对这个锁的控制,lock()方法将让线程A获得锁并且允许它立即执行临界区代码。否则,如果其他线程B正在执行这个锁保护的临界区代码,lock()方法将让线程A休眠直到线程B执行完临界区的代码。

  在线程离开临界区的时候,我们必须使用unlock()方法来释放它持有的锁,以便其它线程可以访问临界区。如果在离开临界区的时候没有调用unlock()方法,其它线程将永远等待,从而导致了死锁(Deadlock)情景。如果在临界区使用了try-catch块,不要忘记将unlock()方法放入finally部分,以便避免死锁。

  ReentrantLock类也允许使用递归调用。如果一个线程获取了锁并且进行了递归调用,它将继续持有这个锁,因此调用lock()方法后也将立即返回,并且线程将继续执行递归调用。再者,我们还可以调用其他的方法。

  必须很小心的使用锁,以避免死锁的发生。

时间: 2024-10-03 21:53:34

使用锁实现同步的相关文章

java锁和同步

Java 语言设计中的一大创新就是:第一个把跨平台线程模型和锁模型应用到语言中去,Java 语言包括了跨线程的关键字synchronized 和 volatile,使用关键字和java类库就能够简单的实现线程间的同步.在简化与平台无关的并发程序开发时,它没有使并发程序的编写工作变得繁琐,反而使它变得更容易了. 在这一章,我们详细介绍锁的技术和概念,java中提供了两种锁,一个是使用关键字的锁,还有一种类库提供的锁. synchronized关键字锁 synchronized关键字能够作为函数的修

java基础知识回顾之java Thread类学习(五)--java多线程安全问题(锁)同步的前提

这里举个例子讲解,同步synchronized在什么地方加,以及同步的前提: * 1.必须要有两个以上的线程,才需要同步. * 2.必须是多个线程使用同一个锁. * 3.必须保证同步中只能有一个线程在运行,锁加在哪一块代码 那么我们要思考的地方有:1.知道我们写的哪些是多线程代码 2.明确共享数据 3.明确多线程运行的代码中哪些语句是操作共享数据的.. 4.要确保使用同一个锁. 下面的代码:需求:两个存户分别往银行存钱,每次村100块,分三次存完. class bank{ private int

python并发编程之多进程(二):互斥锁(同步锁)&amp;进程其他属性&amp;进程间通信(queue)&amp;生产者消费者模型

一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终端 #并发运行,效率高,但竞争同一打印终端,带来了打印错乱 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('

java 类锁与对象锁(实例锁)同步问题

众所周知,synchronized可修饰方法和代码块,可作用于类或者对象. 当修饰代码块时,synchronized(object) 作用于对象,只约束该对象. synchronized(class)作用于类,约束类所有的对象. 修饰方法时,synchronized 修饰static方法时,作用于类.修饰非static方法时作用于对象. 注意类锁和对象锁是两个不同的锁,二者不会发生同步关系. 由于static变量,可被static方法调用,也可被非static方法调用,当二者同时被synchro

Java并发(基础知识)——显示锁和同步工具类

显示锁                                                                                     Lock接口是Java 5.0新增的接口,该接口的定义如下: public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long

[java多线程] - 锁机制&amp;同步代码块&amp;信号量

在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同步代码块机制,锁机制,信号量机制. 一.同步代码块 在java的多线程并发开发过程中,我们最常用的方式就是使用同步代码关键字(synchronized).这种方式的使用不是特别复杂,需要注意的只是你需要明确到底同步的是那个对象,只有当同步的对象一致的情况下,才能够控制互斥的操作.一般情况下,我们会同

Java04 线程同步问题解决——线程锁(同步锁、互斥锁)

目录 [TOC] 写在最前: 可能有误,请大家批评指正 一.线程切换 Java中,如果要实现在一个线程间的线程切换,需要在线程中使用Thread.yield()即可让出CPU时间. 二.线程锁(也叫同步锁.互斥锁) 线程锁可以在有效缩小同步范围的同时,尽可能的保证并发效率 2.1 使用synchronized关键字对方法进行加锁 对整个线程处理加锁(严重影响效率,不常用) 2.1.1 语法 public synchronized void test(){ } 2.1.2 案例 package c

无锁的同步策略——CAS操作详解

1. 从乐观锁和悲观锁谈起 乐观锁和悲观锁是两种不同的解决并发问题的策略.悲观锁策略假定任何一次并发都会发生冲突,所以总是采用最严格的方式来进行并发控制.java中的独占锁(synchronized和重入锁)就是典型悲观锁实现,它只允许线程互斥的访问临界区,也就是阻塞式的同步方式.而乐观锁策略假定大部分情况下并发冲突不会发生,采用的是一种更为宽松的方式来进行并发控制.比如我们马上就要讲的CAS操作.它允许多线程非阻塞式地对共享资源进行修改,但同一时刻只有一个线程能够成功,其他线程被告知失败但并不

有关多线程(同步锁,递归锁,同步对象,信号量)

上面一个随笔已经简单介绍了多线程,比如下面在举个简单的例子: 1 #!/usr/bin/env python 2 #-*-coding:utf-8 -*- 3 4 import threading 5 import time 6 7 def add(): 8 sum = 0 9 10 for i in range(1000000): 11 sum += i 12 13 print("sum: ",sum) 14 15 16 def mul(): 17 sum2 = 1 18 for i