浅谈Synchronized:
synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其他线程
便只能一直等待,等待获取锁的线程释放锁,而获取锁的线程释放锁会有三种情况:
1).获取锁的线程执行完该代码块,然后线程释放对锁的占有;
2).线程执行发生异常,此时JVM会让线程自动释放锁;
3).调用wait方法,在等待的时候立即释放锁,方便其他的线程使用锁.
Lock的特性:
1).Lock不是Java语言内置的;
2).synchronized是在JVM层面上实现的,如果代码执行出现异常,JVM会自动释放锁,但是Lock不行,要保证锁一定会被释放,就必须将unLock放到finally{}中(手动释放);
3).在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍;
4).ReentrantLock增加了锁:
a. void lock(); // 无条件的锁;
b. void lockInterruptibly throws InterruptedException;//可中断的锁;
解释: 使用ReentrantLock如果获取了锁立即返回,如果没有获取锁,当前线程处于休眠状态,直到获得锁或者当前线程可以被别的线程中断去做其他的事情;但是如果是synchronized的话,如果没有获取到锁,则会一直等待下去;
c. boolean tryLock();//如果获取了锁立即返回true,如果别的线程正持有,立即返回false,不会等待;
d. boolean tryLock(long timeout,TimeUnit unit);//如果获取了锁立即返回true,如果别的线程正持有锁,会等待参数给的时间,在等待的过程中,如果获取锁,则返回true,如果等待超时,返回false;
5)Lock的ReentrantReadWriteLock读写锁 适用于 读多写少的场景 效率较高
Condition的特性:
1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。
2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒”读线程”;当从缓冲区读出数据之后,唤醒”写线程”;并且当缓冲区满的时候,”写线程”需要等待;当缓冲区为空时,”读线程”需要等待。
如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒”读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒”读线程”,而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。
实例
ListAdd.java 使用synchronized
package ThreadCollections;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 胖大星 on 2017/9/12.
*/
public class ListAdd {
private volatile static List list = new ArrayList();
public void addList(){
list.add("hello");
}
public int size(){
return list.size();
}
public static void main(String[] agrs){
final ListAdd list1 = new ListAdd();
Object lock = new Object();
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
try {
synchronized (lock){
for (int i = 0; i < 10; i++) {
list1.addList();
System.out.println("当前线程为: "+Thread.currentThread().getName()+" "+i);
Thread.sleep(500);
if(list.size() == 5){
System.out.println("当前通知已发出!");
lock.notify();
}
}
}
}
catch (InterruptedException e){
e.printStackTrace();
}
}
},"t1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if (list1.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("当前线程:" + Thread.currentThread().getName()+"已经收到通知");
throw new RuntimeException();
}
}
},"t2");
thread2.start();
thread.start();
}
}
UseReentrantLock.java 使用Lock
package Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by 胖大星 on 2017/9/17.
* Lock 比 synchronized 方法更加便捷,灵活
*/
public class UseReentrantLock {
//new ReentrantLock(false) 可以带参数 false 表示非公平锁(根据cpu分配优先执行) true表示公平锁(先来先执行)
private ReentrantLock lock = new ReentrantLock();
public void method1(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入method1...");
Thread.sleep(1000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出method1...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void method2(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入method2...");
Thread.sleep(1000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出method2...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
UseReentrantLock useReentrantLock = new UseReentrantLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
useReentrantLock.method1();
useReentrantLock.method2();
}
}, "t1");
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
UseManyCondition.java 使用多个Condition
package Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by 胖大星 on 2017/9/17.
*启动多个 Condition 并行锁 同时并行多个线程
*/
public class UseManyCondition {
private ReentrantLock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
public void m1(){
try {
lock.lock();
System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");
c1.await();
System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m2(){
try {
lock.lock();
System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");
c1.await();
System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m3(){
try {
lock.lock();
System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");
c2.await();
System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m4(){
try {
lock.lock();
System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
c1.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m5(){
try {
lock.lock();
System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
final UseManyCondition umc = new UseManyCondition();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
umc.m1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
umc.m2();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
umc.m3();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
umc.m4();
}
},"t4");
Thread t5 = new Thread(new Runnable() {
@Override
public void run() {
umc.m5();
}
},"t5");
t1.start(); // c1
t2.start(); // c1
t3.start(); // c2
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t4.start(); // c1
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t5.start(); // c2
}
}
UseReentrantReadWriteLock.java Lock使用读写锁
package Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by 胖大星 on 2017/9/17.
* lock的ReentrantReadWriteLock读写锁 适用于 读多写少的场景 效率较高
*/
public class UseReentrantReadWriteLock {
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public void read(){
try {
readLock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入....");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出....");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readLock.unlock();
}
}
public void write(){
try {
writeLock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入....");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出....");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
UseReentrantReadWriteLock useReentrantReadWriteLock = new UseReentrantReadWriteLock();
Thread threadRead1 = new Thread(new Runnable() {
@Override
public void run() {
useReentrantReadWriteLock.read();
}
}, "threadRead1");
Thread threadRead2 = new Thread(new Runnable() {
@Override
public void run() {
useReentrantReadWriteLock.read();
}
}, "threadRead2");
Thread threadWrite1 = new Thread(new Runnable() {
@Override
public void run() {
useReentrantReadWriteLock.write();
}
}, "threafWrite1");
Thread threadWrite2 = new Thread(new Runnable() {
@Override
public void run() {
useReentrantReadWriteLock.write();
}
}, "threafWrite2");
//两个读线程同时开始 ,写线程明显较慢
threadRead1.start();
// threadRead2.start();
threadWrite1.start();
// threadWrite2.start();
}
}