Java同步关键字
synchronized
如果代码中有一段代码被synchronized代码块包含起来了,那么线程中的run()方法执行到这段代码时,其他线程不能执行这段代码。
用一段代码同时只能由一个线程执行。
sample
package com.mrbcy.bigdata.basic.thread.testthread;
public class MySynchronized {
public static void main(String[] args) {
final MySynchronized mySynchronized1 = new MySynchronized();
final MySynchronized mySynchronized2 = new MySynchronized();
new Thread("thread1"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
synchronized (mySynchronized1) {
System.out.println(this.getName() + " start working...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.getName() + " finish work");
}
}
}.start();
new Thread("thread2"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
synchronized (mySynchronized1) {
System.out.println(this.getName() + " start working...");
}
}
}.start();
}
}
输出结果如下:
thread1 start working...
thread1 finish work
thread2 start working...
这是因为我们在两个线程的synchronized块中指定对象时都使用了mySynchronized1,此时只能有一个线程同时进入synchronized块。如果它们指定对象不同,则两线程可以同时运行。下面我们来验证这一点。
把Thread2中的synchronized对象改成mySynchronized2。
new Thread("thread2"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
synchronized (mySynchronized2) {
System.out.println(this.getName() + " start working...");
}
}
}.start();
输出结果如下:
thread1 start working...
thread2 start working...
thread1 finish work
synchronized还可以直接修饰一个方法。
synchronized缺陷
如果一个获得锁的线程一直不退出,其他的线程都无法获得锁,会阻塞所有的其他线程。但是如果获得锁的线程发生异常,jvm会把锁释放掉。
Lock
lock可以实现在无法获取锁时无需阻塞等待,去做其他事。
Lock api示例
简单的lock和tryLock
package com.mrbcy.bigdata.basic.thread.lock;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLockTest {
static Lock lock = new ReentrantLock();
private static ArrayList<Integer> arrayList = new ArrayList<Integer>();
public static void main(String[] args) {
new Thread("thread1"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
lock.lock();
try{
System.out.println(this.getName() + "得到了锁");
for(int i = 0; i < 5; i++){
arrayList.add(i);
}
}catch (Exception e) {
// TODO: handle exception
}finally{
System.out.println(this.getName() + "释放了锁");
}
}
}.start();
new Thread("thread2"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
boolean lockResult = lock.tryLock();
if(lockResult){
try{
System.out.println(this.getName() + "得到了锁");
for(int i = 0; i < 5; i++){
arrayList.add(i);
}
}catch (Exception e) {
// TODO: handle exception
}finally{
System.out.println(this.getName() + "释放了锁");
}
}
}
}.start();
}
}
lockInterruptibly的使用示例。
package com.mrbcy.bigdata.basic.thread.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 观察现象:如果thread-0得到了锁,阻塞。。。thread-1尝试获取锁,如果拿不到,则可以被中断等待
* @author
*
*/
public class MyInterruptibly {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
MyInterruptibly test = new MyInterruptibly();
MyThread thread0 = new MyThread(test);
MyThread thread1 = new MyThread(test);
thread0.start();
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
System.out.println("=====================");
}
public void insert(Thread thread) throws InterruptedException{
lock.lockInterruptibly(); //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
try {
System.out.println(thread.getName()+"得到了锁");
long startTime = System.currentTimeMillis();
for( ; ;) {
if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
//插入数据
}
}
finally {
System.out.println(Thread.currentThread().getName()+"执行finally");
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
}
class MyThread extends Thread {
private MyInterruptibly test = null;
public MyThread(MyInterruptibly test) {
this.test = test;
}
@Override
public void run() {
try {
test.insert(Thread.currentThread());
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+"被中断");
}
}
}
稍微说明一下,两个线程都是用lock.lockInterruptibly()
来获取锁,如果有的线程没有获取到锁,可以调用interrupt方法中断它,它就可以继续做别的事了。注意被中断的线程会抛出一个异常
ReadWriteLock
对于数据来说,多个线程同时读不会产生影响。
ReadWriteLock是一个接口。利用它可以实现分别的获取读锁或写锁来提高应用的性能。
如果一组线程中没有线程拿到写锁,所有的线程都可以读。如果有一个线程拿到了写锁,所有的其他线程都不能读也不能写了。
ReadWriteLock 示例
用synchronized来实现的代码示例
package com.mrbcy.bigdata.basic.thread.lock;
/**
* 一个线程又要读又要写,用synchronize来实现的话,读写操作都只能锁住后一个线程一个线程地进行
* @author
*
*/
public class MySynchronizedReadWrite {
public static void main(String[] args) {
final MySynchronizedReadWrite test = new MySynchronizedReadWrite();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
public synchronized void get(Thread thread) {
long start = System.currentTimeMillis();
int i=0;
while(System.currentTimeMillis() - start <= 1) {
i++;
if(i%4==0){
System.out.println(thread.getName()+"正在进行写操作");
}else {
System.out.println(thread.getName()+"正在进行读操作");
}
}
System.out.println(thread.getName()+"读写操作完毕");
}
}
使用synchronized时读写只能一起锁定,其他线程只能等待。
使用ReadWriteLock实现的代码示例:
package com.mrbcy.bigdata.basic.thread.lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 使用读写锁,可以实现读写分离锁定,读操作并发进行,写操作锁定单个线程
*
* 如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。
* 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
* @author
*
*/
public class MyReentrantReadWriteLock {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final MyReentrantReadWriteLock test = new MyReentrantReadWriteLock();
new Thread(){
public void run() {
test.get(Thread.currentThread());
test.write(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
test.write(Thread.currentThread());
};
}.start();
}
/**
* 读操作,用读锁来锁定
* @param thread
*/
public void get(Thread thread) {
rwl.readLock().lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
} finally {
rwl.readLock().unlock();
}
}
/**
* 写操作,用写锁来锁定
* @param thread
*/
public void write(Thread thread) {
rwl.writeLock().lock();;
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在进行写操作");
}
System.out.println(thread.getName()+"写操作完毕");
} finally {
rwl.writeLock().unlock();
}
}
}
使用读写锁,读可以并行执行。如果有其他线程申请了读锁,有线程想申请写锁,它必须等待。如果有个线程已经申请了写锁,所有申请读锁的线程必须等待。
Lock和synchronized的区别
- Lock不是Java的关键字,是
java.util.concurrent.locks
包下的一个接口。 - synchronized不需要用户手动的释放锁。Lock必须手动释放锁。
- Lock可以实现读写锁分离,在某些场景下可以提高效率。
- Lock可以中断等待的线程,在某些场景下可以提高效率。
- 通过Lock可以知道是否拿到了锁。
从性能上说,如果资源竞争不是很激烈,两者性能相仿。如果资源竞争很激烈,Lock的性能显著高于synchronized。