变量的值在使用之前总会从主内存中再读取出来。
对变量值的修改总会在完成之后写回到主内存中。
volatile不能保证多线程中的同步,但是可以保证在多线程中数据是可见的。
(3)、显式锁 Lock 和 ReentrantLock
与内置锁机制不同的是,Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁方法都是显示的。
lock能实现synchronized实现的所有功能,lock()的使用更加方便灵活、功能更加强大。
lock分别为读、写提供了锁,这样效率更高。
public interface Lock {
//获取锁
void lock();
//如果当前线程未被中断,则获取锁。
void lockInterruptibly() throws InterruptedException;
//仅在调用时锁为空闲状态才获取锁
tryLock();
//如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
tryLock(long time,TimeUnit unit);
//返回绑定此Lock实例的新Condition实例。
Condition newCondition();
}lock方法获取不到锁则阻塞,tryLock获取不到锁不阻塞直接返回false。
lock带来灵活、方便的同时也引入隐患就是死锁,lock的释放unlock需要在finally代码块里释放锁,如果没有释放锁很可能程序运行不下去,造成死锁。
Lock lock = new ReentrantLock();
lock.lock();
try {
// update object state
}
finally {
lock.unlock();
}
必须在finally 中来释放Lock
(4)、锁的唤醒:
notify 和 notifyAll:
在条件队列API中有两个发出通知的方法,即 notify和 notifyAll。无论调用哪一个,都必须持有与条件队列对象相关联的锁。notify操作会选在条件队列中的一个线程唤醒,其他线程则无法得到唤醒通知。而notifyAll则会唤醒所有等待在条件队列中的线程来竞争锁。使用notify单一的同时可能发生信号丢失问题。
普遍认可的做法是优先使用notifyAll而不是notify。虽然notifyAll可能比notify更低效,但却更容易确保类的行为是正确的。
注意:notify()对于只有一个线程时是有意义的,多线程时需要使用notifyAll(),由于notify()和notifyAll()不精确,因此建议使用信号量或阻塞队列来实现对共享资源的加锁。
2、并发集合ConcurrentHashMap
先说下HashMap:其是线程不安全的,没有同步的机制,在多线程执行时可能会造成数据的错乱,并且效率也低。HashMap底层实现机制是一个数组,数组中的每个元素又是一个单链表,只有hashcode值冲突的元素才放到这个单链表中。HashMap中的元素是由key与value封装成的Entity对象,这是造成其占用很大内存空间的根本原因,其次是他的默认大小是16,在需要增长时增长到原来的2倍,并且还是2的N次方。
ConcurrentHashMap即使线程安全的又是高效的。通过源码分析他的底层实现机制是一个segment数组,即段数组。将数组中的元素分为若干段,每一段对应一个锁,这可以达到锁分离的目的,对其中一段的访问不影响其他的元素,提交了效率。他的并发度是16,是构造器中的一个参数。
3、通过共享对象达到共享数据的目的
(1)、设计线程安全的类。
找出构造对象状态的所有变量。
约束状态变量的不变性条件。
建立对象状态的并发访问管理策略。
(2)、实例封闭
如果某对象不是线程安全的,我们可以通过多种技术使其在多线程中能安全的使用。确保该对象只能由单个线程访问。
public class AnimalSet{
private final Set
public sychronized void addAnimaln(Animal p) {
mySet.add(p)
}
public sychronized boolean containsAnimal(Animal p) {
return mySet.contains(p);
}
}
虽然HashSet并非线程安全的。但是mySet是私有的不会逸出。唯一能访问mySet的代码是addPerson()和containsPerson()。
在执行上他们都要获的PersonSet 上的锁。PersonSet的状态完全又它的内置锁保护。所以AnimalSet是一个线程安全的类。
4、基础模块
(1)、同步容器类。线程安全的容器包括Vector和Hashtable。同步的封装容器类由Collections.sychronizedXXX工厂方法创建。
eg:synchronizedList,synchronizedMap(m)、synchronizedSet(s)这些是通过synchronized同步方法来实现的,达到了线程安全但是效率低
(2)、同步工具类。
阻塞队列(BlockingQueue(LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,SynchronousQueue))不仅可以保存对象的容器,而且还可以协调生产者和消费者之间的控制流。
信号量(Semaphore):用来控制同时访问某个特定资源的操作数量。通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。Semaphore允许线程获取许可, 未获得许可的线程需要等待.这样防止了在同一时间有太多的线程执行。Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
eg:模拟30辆车去泊车,而车位有10个的场景. 当车位满时,出来一辆车,才能有一辆车进入停车.
转载 http://mouselearnjava.iteye.com/blog/1921468
package my.concurrent.semaphore;
import java.util.concurrent.Semaphore;
public class Car implements Runnable {
private final Semaphore parkingSlot;
private int carNo;
/**
* @param parkingSlot
* @param carName
*/
public Car(Semaphore parkingSlot, int carNo) {
this.parkingSlot = parkingSlot;
this.carNo = carNo;
}
public void run() {
try {
parkingSlot.acquire();
parking();
sleep(300);
parkingSlot.release();
leaving();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void parking() {
System.out.println(String.format("%d号车泊车", carNo));
}
private void leaving() {
System.out.println(String.format("%d号车离开车位", carNo));
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package my.concurrent.semaphore;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class ParkingCars {
private static final int NUMBER_OF_CARS = 30;
private static final int NUMBER_OF_PARKING_SLOT = 10;
public static void main(String[] args) {
/*
* 采用FIFO, 设置true
*/
Semaphore parkingSlot = new Semaphore(NUMBER_OF_PARKING_SLOT, true);
ExecutorService service = Executors.newCachedThreadPool();
for (int carNo = 1; carNo <= NUMBER_OF_CARS; carNo++) {
service.execute(new Car(parkingSlot, carNo));
}
sleep(3000);
service.shutdown();
/*
* 输出还有几个可以用的资源数
*/
System.out.println(parkingSlot.availablePermits() + " 个停车位可以用!");
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}