线程同步机制(一)--Synchronized,Lock

多个执行线程共享一个资源的情形是最常见的并发编程情景之一。在并发应用中常常遇到这样的情景:多个线程读或者写相同的数据,或者访问相同的文件或者数据库连接。为了防止这些共享资源可能出现错误或者数据不一致,人们引入了临界区(critical section)概念。临界区是一个用以访问共享资源的代码块,这个代码块中同一时间只允许一个线程执行。

为了实现这个临界区,Java提供了同步机制。当一个线程试图访问一个临界区时,它将使用一种同步机制来查看是不是已经有其他线程进入临界区。如果没有,它就进入临界区,如果有,他就被同步机制挂起,直到进入的线程离开临界区。如果在等待进入临界区的线程不止一个,JVM会选择其中的一个,其余将继续等待。

一、使用synchronized实现同步方法。如果一个对象已用synchronized关键字来生命,那么只有一个执行线程被允许访问它。如果其他线程试图访问这个对象的其他方法,它将被挂起,知道第一个线程执行完正在运行的方法。

也就是说,每一个用synchronized关键字声明的方法都是临界区。静态方法则有不同的行为。用synchronized关键字声明的静态方法,同时只能够被一个线程访问,但是其他线程可以访问这个对象的其他非静态方法。必须谨慎这一点。

package org.concurrency.synchronization;/**
 * @author Administrator
 * 银行账户模型
 */public class Account {
	private double balance;//余额

	public double getBalance() {		return balance;
	}	public void setBalance(double balance) {		this.balance = balance;
	}	/**
	 * 转账,使余额增加
	 * */
	public synchronized void addAccount(double amount){		double tmp = balance;
		System.out.printf("before_add_Account : Current Balance: %f\n",tmp);		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		tmp += amount;		this.balance = tmp;
	}	/**
	 * 转出,使余额减少
	 * */
	public synchronized void subtractAmount(double amount){		double tmp = balance;
		System.out.printf("before_subtract_Account : Current Balance: %f\n",tmp);		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		tmp -= amount;		this.balance = tmp;
		System.out.printf("subtract_Account : Current Balance: %f\n",this.getBalance());
	}
}package org.concurrency.synchronization;/**
 * @author Administrator
 * ATM模拟类
 */public class Bank implements Runnable {
	private Account account;
	public Bank(Account account) {		super();		this.account = account;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		for(int i = 0;i<10;i++){
			account.subtractAmount(1000);
		}
	}

}package org.concurrency.synchronization;/**
 * @author Administrator
 * 公司模拟类
 */public class Company implements Runnable {
	private Account account;
	public Company(Account account) {		super();		this.account = account;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		for(int i = 0;i<10;i++){
			account.addAccount(1000);
		}
	}
}package org.concurrency.synchronization;/**
 * @author Administrator
 * 线程启动类
 */public class Main {

	public static void main(String[] args) {		// TODO Auto-generated method stub
		Account account = new Account();
		account.setBalance(1000);

		Company company = new Company(account);
		Thread companyThread = new Thread(company);

		Bank bank = new Bank(account);
		Thread bankThread = new Thread(bank);

		companyThread.start();
		bankThread.start();
		try {
			companyThread.join();
			bankThread.join();

			System.out.printf("Account : Final Balance: %f\n",account.getBalance());
		} catch (InterruptedException e) {			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

运行结果:
before_add_Account : Current Balance: 1000.000000before_add_Account : Current Balance: 2000.000000before_add_Account : Current Balance: 3000.000000before_subtract_Account : Current Balance: 4000.000000subtract_Account : Current Balance: 3000.000000before_subtract_Account : Current Balance: 3000.000000subtract_Account : Current Balance: 2000.000000before_subtract_Account : Current Balance: 2000.000000subtract_Account : Current Balance: 1000.000000before_subtract_Account : Current Balance: 1000.000000subtract_Account : Current Balance: 0.000000before_subtract_Account : Current Balance: 0.000000subtract_Account : Current Balance: -1000.000000before_subtract_Account : Current Balance: -1000.000000subtract_Account : Current Balance: -2000.000000before_subtract_Account : Current Balance: -2000.000000subtract_Account : Current Balance: -3000.000000before_subtract_Account : Current Balance: -3000.000000subtract_Account : Current Balance: -4000.000000before_subtract_Account : Current Balance: -4000.000000subtract_Account : Current Balance: -5000.000000before_subtract_Account : Current Balance: -5000.000000subtract_Account : Current Balance: -6000.000000before_add_Account : Current Balance: -6000.000000before_add_Account : Current Balance: -5000.000000before_add_Account : Current Balance: -4000.000000before_add_Account : Current Balance: -3000.000000before_add_Account : Current Balance: -2000.000000before_add_Account : Current Balance: -1000.000000before_add_Account : Current Balance: 0.000000Account : Final Balance: 1000.000000

在示例中使用了临时变量,故意制造了一个错误场景:tmp先获取账户余额,然后进行数额累加,最后把最终结果更新为账户余额。此外,通过Thread.sleep()方法增加了延时,使得正在执行的线程休眠,而此时其他线程也可能执行这个方法,因此可能会变更余额,引发错误。而synchronized关键字避免了这类错误的发生。

在没有synchronized关键字的情况下,一个线程读取了账户余额然后进入休眠,这个时候其他线程读取这个账户余额,最终这两个方法都将修改同一个余额,这将产生数据不一致,去除synchronized关键字,运行结果,最初和最后的金额不一致,并且多次运行,将获取多个不同的结果:

before_subtract_Account : Current Balance: 1000.000000
before_add_Account : Current Balance: 1000.000000
before_add_Account : Current Balance: 0.000000
subtract_Account : Current Balance: 0.000000
before_subtract_Account : Current Balance: 0.000000
subtract_Account : Current Balance: -1000.000000
before_subtract_Account : Current Balance: -1000.000000
before_add_Account : Current Balance: -1000.000000
before_add_Account : Current Balance: -2000.000000
subtract_Account : Current Balance: -2000.000000
before_subtract_Account : Current Balance: -2000.000000
before_add_Account : Current Balance: -1000.000000
subtract_Account : Current Balance: -1000.000000
before_subtract_Account : Current Balance: -1000.000000
before_add_Account : Current Balance: 0.000000
subtract_Account : Current Balance: -2000.000000
before_subtract_Account : Current Balance: -2000.000000
subtract_Account : Current Balance: 1000.000000
before_subtract_Account : Current Balance: 1000.000000
before_add_Account : Current Balance: 1000.000000
subtract_Account : Current Balance: 0.000000
before_subtract_Account : Current Balance: 0.000000
before_add_Account : Current Balance: 2000.000000
subtract_Account : Current Balance: -1000.000000
before_subtract_Account : Current Balance: -1000.000000
before_add_Account : Current Balance: 3000.000000
subtract_Account : Current Balance: -2000.000000
before_subtract_Account : Current Balance: -2000.000000
before_add_Account : Current Balance: 4000.000000
subtract_Account : Current Balance: -3000.000000
Account : Final Balance: 5000.000000

synchronized关键字会降低应用程序的性能,因此只能在并发情况中需要修改共享数据的方法上使用它。如果多个线程访问同一个synchronized方法,则只有一个线程可以访问,其他线程将等待。

可以递归调用被synchronized声明的方法。当线程访问一个对象的同步方法时,它还可调用这个对象的其他同步方法。

我们通过synchronized关键字来保护代码块的访问。应该这样利用synchronized关键字:方法的其余部分保持在synchronized代码块之外,以获取更好的性能。临界区(同一时间只能被一个线程访问的代码块)的访问应该尽可能的短。

二、使用非依赖属性实现同步。当使用synchronized关键字来保护代码块时,必须把对象引用作为传入参数。通常情况下,使用this关键字来引用执行方法所属对象。在类中有多个非依赖来属性,他们被多个线程共享,则必须同步每一个变量的访问,同一时刻只允许一个线程访问一个属性变量,其他线程访问令一个属性变量。下面实例将演示一个电影院售piao的场景。

package org.concurrency.synchronization;/**
 * @author Administrator
 * 电影院类
 */public class Cinema {
	private long vacanciesCinema1;	private long vacanciesCinema2;
	private final Object controlCinema1;	private final Object controlCinema2;	public Cinema() {
		controlCinema1 = new Object();
		controlCinema2 = new Object();
		 vacanciesCinema1 = 20;
		 vacanciesCinema2 = 20;
	}
	/**
	 * 卖出piao
	 * */
	public boolean selTickets1(int number){		synchronized (controlCinema1) {			if(number <vacanciesCinema1){
				vacanciesCinema1 -= number;
				System.out.println("vacanciesCinema1:卖出"+number+"张");				return true;
			}else{				return false;
			}
		}
	}	public boolean selTickets2(int number){		synchronized (controlCinema2) {
			if(number <vacanciesCinema2){
				vacanciesCinema2 -= number;
				System.out.println("vacanciesCinema2:卖出"+number+"张");				return true;
			}else{				return false;
			}
		}
	}	public boolean returnTickets1(int number){		synchronized (controlCinema1) {
			vacanciesCinema1 += number;
			System.out.println("vacanciesCinema1:退回"+number+"张");			return true;
		}
	}	public boolean returnTickets2(int number){		synchronized (controlCinema2) {
			vacanciesCinema2 += number;
			System.out.println("vacanciesCinema2:退回"+number+"张");			return true;
		}
	}	public long getVacanciesCinema1() {		return vacanciesCinema1;
	}	public long getVacanciesCinema2() {		return vacanciesCinema2;
	}

}package org.concurrency.synchronization;/**
 * @author Administrator
 * 售piao处1
 */public class TicketOffice1 implements Runnable {
	private Cinema cinema;
	public TicketOffice1(Cinema cinema) {		super();		this.cinema = cinema;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		cinema.selTickets1(3);
		cinema.selTickets1(2);
		cinema.selTickets2(2);
		cinema.returnTickets1(3);
		cinema.selTickets1(5);
		cinema.selTickets2(2);
		cinema.selTickets2(2);
		cinema.selTickets2(2);
	}

}package org.concurrency.synchronization;/**
 * @author Administrator
 * 售piao处2
 */public class TicketOffice2 implements Runnable {
	private Cinema cinema;
	public TicketOffice2(Cinema cinema) {		super();		this.cinema = cinema;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		cinema.selTickets2(2);
		cinema.selTickets2(4);
		cinema.selTickets1(2);
		cinema.selTickets1(1);
		cinema.returnTickets2(2);
		cinema.selTickets1(3);
		cinema.selTickets2(2);
		cinema.selTickets1(2);
	}

}package org.concurrency.synchronization;/**
 * @author Administrator
 * 主程序
 */public class Main_Cinema {

	public static void main(String[] args) {		// TODO Auto-generated method stub
		Cinema cinema = new Cinema();
		TicketOffice1 ticketOffice1 = new TicketOffice1(cinema);
		Thread thread1 = new Thread(ticketOffice1,"TicketOffice1");

		TicketOffice2 ticketOffice2 = new TicketOffice2(cinema);
		Thread thread2 = new Thread(ticketOffice2,"TicketOffice1");

		thread1.start();
		thread2.start();
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.printf("Room 1 Vacancies: %d\n",cinema.getVacanciesCinema1());
		System.out.printf("Room 2 Vacancies: %d\n",cinema.getVacanciesCinema2());
	}

}

工作原理

用synchronized关键字保护代码块时,我们使用对象作为它的传入参数。JVM保证同一时间只有一个线程能够访问这个对象的代码保护块。这个例子使用了一个对象来控制对vacanciesCinema1属性的访问,所以同一时间只有一个线程能够修改这个属性;使用了另一个对象来控制vacanciesCinema2属性的访问,所以同一时间只有一个线程能够修改这个属性。但是,这个离职允许同事运行两个线程:一个修改vacanciesCinema1属性,另一个修改vacanciesCinema2属性。

运行结果总是如下:

vacanciesCinema1:卖出3张
vacanciesCinema2:卖出2张
vacanciesCinema2:卖出4张
vacanciesCinema1:卖出2张
vacanciesCinema1:卖出2张
vacanciesCinema2:卖出2张
vacanciesCinema1:卖出1张
vacanciesCinema2:退回2张
vacanciesCinema1:退回3张
vacanciesCinema1:卖出5张
vacanciesCinema2:卖出2张
vacanciesCinema1:卖出3张
vacanciesCinema2:卖出2张
vacanciesCinema2:卖出2张
vacanciesCinema1:卖出2张
vacanciesCinema2:卖出2张
Room 1 Vacancies: 5
Room 2 Vacancies: 6

三、在同步代码中使用条件

在并发编程中一个典型的问题就是生产者-消费者(Producer - Consumer)问题。我们有一个数据缓冲区,一个或多个数据生产者将把数据存入这个缓冲区,一个或多个数据消费者将数据从缓冲区取走。

这个缓冲区是一个共享数据结构,必须使用同步机制控制对它的访问,例如使用synchronized关键字,但是会受到更多的限制。如果缓冲区是满的,生产者就不能再放入数据,如果数据是空的,消费者就不能读取数据。

对这些场景,Java在Object类中提供了wait()、notify()和notifyAll()方法。线程可以在同步代码块中使用wait()方法。不能再同步代码块中调用wait()方法。当一个线程调用wait()方法时,JVM将这个线程置入休眠,并且释放控制这个同步代码块的对象,同时允许其他线程执行这个对象控制的其他同步代码块。为了唤醒这个线程,必须在这个对象控制的某个同步代码块儿中调用notify()或者notifyAll()方法。

package org.concurrency.synchronization.producer;import java.util.Date;import java.util.LinkedList;import java.util.List;public class EventStorage {
	private int maxSize;	private List<Date> storage;	public EventStorage() {
		maxSize = 0;
		storage = new LinkedList<Date>();
	}
	public synchronized void set(){		while(storage.size() == maxSize){			try {
				wait();
			} catch (InterruptedException e) {				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		storage.add(new Date());
		System.out.printf("Set : %d",storage.size());
		notifyAll();
	}
	public synchronized void get(){		while(storage.size() == 0){			try {
				wait();
			} catch (InterruptedException e) {				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.printf("Get: %d: %s",storage.size(),((LinkedList<?>)storage).poll());
		notifyAll();
	}
}package org.concurrency.synchronization.producer;public class Producer implements Runnable {
	private EventStorage storage;
	public Producer(EventStorage storage) {		super();		this.storage = storage;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		for(int i = 0;i<10;i++){
			storage.set();
		}
	}

}package org.concurrency.synchronization.producer;public class Producer implements Runnable {
	private EventStorage storage;
	public Producer(EventStorage storage) {		super();		this.storage = storage;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		for(int i = 0;i<10;i++){
			storage.set();
		}
	}

}package org.concurrency.synchronization.producer;public class Consumer implements Runnable {
	private EventStorage storage;
	public Consumer(EventStorage storage) {		super();		this.storage = storage;
	}	@Override
	public void run() {		// TODO Auto-generated method stub
		for(int i = 0;i<10;i++){
			storage.get();
		}
	}

}package org.concurrency.synchronization.producer;public class Main_Producer {

	public static void main(String[] args) {		// TODO Auto-generated method stub
		EventStorage storage = new EventStorage();
		Thread thread1 = new Thread(new Producer(storage));
		Thread thread2 = new Thread(new Consumer(storage));

		thread1.start();
		thread2.start();

	}

}

四、使用锁实现同步

Java提供了基于Lock接口及其实现类的同步机制,它比synchronized关键字更强大也更灵活。

  • 支持更灵活的同步代码块结构。使用synchronized关键字时,只能在同一个synchronized块结构中获取和释放控制。Lock接口允许实现更复杂的临界区结构(即控制的获取和释放不在同一个块结构中)
  • 相比synchronized关键字,Lock借口提供了更多的功能。其中一个新功能是tryLock()方法的实现。这个方法试图获取锁,如果锁已经被其他线程获取,它将返回false并继续往下执行代码。
  • Lock接口允许分离读和写的操作,允许多个读线程和只有一个写线程

示例学习如何使用锁来同步代码,并且使用Lock接口和它的实现类--ReentrantLock类来创建一个临界区。

package org.concurrency.synchronization.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author kucs
 * 创建一个打印队列类
 */
public class PrintQueue {
 //声明一个锁对象
 private final Lock queueLock = new ReentrantLock();
 //实现打印方法printJob()
 public void printJob(Object document){
  //在打印方法内部,通过调用lock()方法获取对锁对象的控制
  queueLock.lock();
  //打印模拟文档
  try {
   Long duration = (long) (Math.random() * 10000);
   System.out.println(Thread.currentThread().getName()+":"
     + "PrintQueue:Printing a Job during "+(duration/1000)+" seconds");
   Thread.sleep(duration);
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }finally{
   //通过unlock()方法释放对锁对象的控制
   queueLock.unlock();
  }
 }
}

package org.concurrency.synchronization.lock;
/**
 * @author Administrator
 * 创建打印工作类Job
 */
public class Job implements Runnable {
 private PrintQueue printQueue;
 
 public Job(PrintQueue printQueue) {
  this.printQueue = printQueue;
 }
 @Override
 public void run() {
  // TODO Auto-generated method stub
  System.out.printf("%s: Gong 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());
 }
}

package org.concurrency.synchronization.lock;
public class Main {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  PrintQueue printQueue = new PrintQueue();
  Thread[] threads = new Thread[10];
  for(int i = 0;i < threads.length;i++){
   threads[i] = new Thread(new Job(printQueue));
  }
  for (Thread thread : threads) {
   thread.start();
  }
 }
}

运行结果:

Thread-0: Gong to print a document
Thread-9: Gong to print a document
Thread-6: Gong to print a document
Thread-8: Gong to print a document
Thread-7: Gong to print a document
Thread-5: Gong to print a document
Thread-4: Gong to print a document
Thread-3: Gong to print a document
Thread-2: Gong to print a document
Thread-1: Gong to print a document
Thread-0:PrintQueue:Printing a Job during 7 seconds
Thread-9:PrintQueue:Printing a Job during 2 seconds
Thread-0: The document has been printed
Thread-9: The document has been printed
Thread-6:PrintQueue:Printing a Job during 1 seconds
Thread-6: The document has been printed
Thread-8:PrintQueue:Printing a Job during 3 seconds
Thread-8: The document has been printed
Thread-7:PrintQueue:Printing a Job during 7 seconds
Thread-7: The document has been printed
Thread-5:PrintQueue:Printing a Job during 9 seconds
Thread-4:PrintQueue:Printing a Job during 4 seconds
Thread-5: The document has been printed
Thread-4: The document has been printed
Thread-3:PrintQueue:Printing a Job during 3 seconds
Thread-3: The document has been printed
Thread-2:PrintQueue:Printing a Job during 4 seconds
Thread-2: The document has been printed
Thread-1:PrintQueue:Printing a Job during 9 seconds
Thread-1: The document has been printed

这个范例主要部分是打印队列PrintQueue中的PrintJob()方法。我们使用锁实现一个临界区,并且保证同一时间只有一个执行线程访问这个临界区时,必须创建ReentrantLock对象。在这个临界区的开始,必须通过lock()方法获取对锁的控制。当线程A访问这个方法时,如果没有其他线程获取对这个锁的控制,lock()方法将让线程A获得锁并且允许它立刻执行临界区代码。否则,如果其他线程B正在执行这个锁保护的临界区代码。lock()方法将让线程A休眠直到线程B执行完临界区代码

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

Lock接口(和它的实现类ReentrantLock)还提供了另一种方法来获取锁,即tryLock()方法。跟lock()方法最大的不同是:线程使用tryLock()不能够获取锁,tryLock()会立即返回,它不会将线程置入睡眠。tryLock()方法返回一个布尔值,true表示线程获取了锁,false表示线程没有获取锁。

注意:tryLock()方法返回false,程序不会执行临界区代码,如果执行了,这样应用可能会出现错误的结果。

五、使用读写锁实现同步数据访问

锁机制最大的改进之一就是ReadWriteLock接口和它的唯一实现类ReentrantReadWriteLock。这个类有两个锁,一个是读操作锁,一个是写操作锁。使用读操作锁时可以允许多个线程同时访问。但是使用写操作锁时只允许一个线程进行。在一个线程执行写操作时,其他线程不能够执行读操作。

我们将通过范例来学习如何使用ReadWriteLock接口编程。这个范例将使用ReadWriteLock接口控制对价格对象的访问,价格对象存储了两个产品的价格。

package org.concurrency.synchronization.lock.readwritelock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @author Administrator 价格信息类
 */
public class PriceInfo {
 private double price1;
 private double price2;
 // 声明读写锁ReadWriteLock对象lock
 private ReadWriteLock lock;
 // 构造器初始化这3个属性
 public PriceInfo() {
  price1 = 1.0;
  price2 = 2.0;
  lock = new ReentrantReadWriteLock();
 }
 public double getPrice1() {
  // 使用读锁来获取对这个属性的访问
  lock.readLock().lock();
  double value = price1;
  lock.readLock().unlock();
  return value;
 }
 public double getPrice2() {
  // 使用读锁来获取对这个属性的访问
  lock.readLock().lock();
  double value = price2;
  lock.readLock().unlock();
  return value;
 }
 public void setPrices(double price1,double price2){
  lock.writeLock().lock();
  this.price1 = price1;
  this.price2 = price2;
  lock.writeLock().unlock();
 }
}

package org.concurrency.synchronization.lock.readwritelock;
/**
 * @author Administrator
 * 读取价格信息类
 */
public class Reader implements Runnable {
 private PriceInfo priceInfo;
 
 public Reader(PriceInfo priceInfo) {
  super();
  this.priceInfo = priceInfo;
 }
 @Override
 public void run() {
  // TODO 循环读取两个价格10此
  for(int i = 0;i<10;i++){
   System.out.printf("%s: Reader: Price 1: %f\n",Thread.currentThread().getName(),priceInfo.getPrice1());
   System.out.printf("%s: Reader: Price 2: %f\n",Thread.currentThread().getName(),priceInfo.getPrice2());
  }
  
 }
}

package org.concurrency.synchronization.lock.readwritelock;
/**
 * @author Administrator
 * 创建写入类Writer,修改价格信息
 */
public class Writer implements Runnable {
 private PriceInfo priceInfo;
 
 public Writer(PriceInfo priceInfo) {
  super();
  this.priceInfo = priceInfo;
 }
 @Override
 public void run() {
  // TODO 修改价格信息3次
  for(int i = 0;i<3;i++){
   System.out.printf("Writer: Attempt to modfy the price.\n");
   priceInfo.setPrices(Math.random() * 10, Math.random() * 8);
   System.out.printf("Writer: Prices have been modified.\n");
   try {
    Thread.sleep(2);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }
}

package org.concurrency.synchronization.lock.readwritelock;
public class Main {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  PriceInfo priceInfo = new PriceInfo();
  //创建5个读取类Reader对象
  Reader[] readers = new Reader[5];
  Thread[] threadReader = new Thread[5];
  
  for(int i = 0;i<5;i++){
   readers[i] = new Reader(priceInfo);
   threadReader[i] = new Thread(readers[i]);
  }
  
  //创建1个写入类对象
  Writer writer = new Writer(priceInfo);
  Thread threadWriter = new Thread(writer);
  
  //启动这6个线程
  for (Thread thread : threadReader) {
   thread.start();
  }
  threadWriter.start();
 }
}

运行结果截图:

Writer: Attempt to modfy the price.
Thread-2: Reader: Price 1: 1.000000
Thread-1: Reader: Price 1: 1.000000
Thread-0: Reader: Price 1: 1.000000
Thread-4: Reader: Price 1: 1.000000
Thread-3: Reader: Price 1: 1.000000
Thread-4: Reader: Price 2: 2.636754
Thread-0: Reader: Price 2: 2.636754
Thread-1: Reader: Price 2: 2.636754
Thread-2: Reader: Price 2: 2.636754
Writer: Prices have been modified.

读操作锁通过ReadWriteLock接口的readLock()方法获取,这个锁实现了Lock接口,所以我们可以使用lock(),unlock()和tryLock()方法。写操作锁是通过ReadWriteLock接口的writeLock()方法获取的,这个锁同样实现了Lock接口

时间: 2024-10-07 16:29:31

线程同步机制(一)--Synchronized,Lock的相关文章

Java同步机制总结--synchronized

不久前用到了同步,现在回过头来对JAVA中的同步做个总结,以对前段时间工作的总结和自我技术的条理话.JAVA中synchronized关键字能够 作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.假如再细的分类,synchronized可作用于instance变 量.object reference(对象引用).static函数和class literals(类名称字面常量)身上. 在进一步阐述之前,我们需要明确几点: A.无论synchronized关键字加在方法上还

线程同步机制(二)-- 线程同步辅助类

我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了同步和临界区的概念,并且讨论了多个并发任务共享一个资源时的同步情况.访问共享资源的代码块叫临界区. 我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了一下内容: synchronized关键字 Lock接口及其实现类,如ReentrantLock,ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock 本章我们将学习如

【总结】Java线程同步机制深刻阐述

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

Python多线程(2)——线程同步机制

本文介绍Python中的线程同步对象,主要涉及 thread 和 threading 模块. threading 模块提供的线程同步原语包括:Lock.RLock.Condition.Event.Semaphore等对象. 1. Lock 1.1 Lock对象的创建 Lock是Python中最底层的同步机制,直接由底层模块 thread 实现,每个lock对象只有两种状态——上锁和未上锁,不同于下文的RLock对象,Lock对象是不可重入的,也没有所属的线程这个概念. 可以通过下面两种方式创建一

【转】Python线程同步机制: Locks, RLocks, Semaphores, Conditions, Events和Queues

Python线程同步机制: Locks, RLocks, Semaphores, Conditions, Events和Queues | Comments 翻译自Laurent Luce的博客原文名称:Python threads synchronization: Locks, RLocks, Semaphores, Conditions, Events and Queues原文连接:http://www.laurentluce.com/posts/python-threads-synchron

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

分析.Net里线程同步机制

我们知道并行编程模型两种:一种是基于消息式的,第二种是基于共享内存式的. 前段时间项目中遇到了第二种 使用多线程开发并行程序共享资源的问题 ,今天以实际案例出发对.net里的共享内存式的线程同步机制做个总结,由于某些类库的应用属于基础,所以本次不对基本使用做出讲解,基本使用 MSDN是最好的教程. 一.volatile关键字      基本介绍: 封装了 Thread.VolatileWrite() 和  Thread.VolatileRead()的实现 ,主要作用是强制刷新高速缓存.     

linux学习笔记之线程同步机制

一.基础知识. 1:线程同步机制:互斥量,读写锁,条件变量,自旋锁,屏障. 1,互斥量:每个进程访问被互斥量保护的资源时,都需要先对互斥量进行判断. 1)互斥量重要属性:进程共享属性,健壮属性,类型属性. 2)部分系统 不支持 进程共享属性 3)对互斥量重复加锁会导致死锁. 2,读写锁. 1)读写锁有3种状态:读模式加锁,写模式加锁,未加锁. 1-写加锁模式:任何加锁都会被阻塞. 2-读加锁模式:读模式加锁的任何线程都可以得到访问权,同时添加一个读模式锁.但,写模式加锁会被阻塞. 3-在读模式下

线程同步Volatile与Synchronized(一)

volatile 一.volatile修饰的变量具有内存可见性 volatile是变量修饰符,其修饰的变量具有内存可见性. 可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值. 在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存. 二.volatile禁止指令重排 volatile

线程同步机制之互斥锁通信机制

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; #define WORK_SIZE 1024 char work_area[WORK_SIZE]; int time_to_exit=0; int main(int argc,