今天我在学习集合框架想到一个问题,发现自己对锁不太了解
起因:
Hashtable是线程安全的,原因是在方法加了synchronized关键词
public synchronized V put(K key, V value) {}
public synchronized V get(Object key) {}
我们在获得get方法后还可以调用put方法么,显然是不可以的,如果可以就不是线程安全了。
为什么:
我们将锁分为: 方法锁,对象锁,类锁
synchronized,这个东西我们一般称之为”同步锁“,他在修饰代码块的时候需要传入一个引用对象作为“锁”的对象。
方法锁:
通过在方法声明中加入synchronized关键字来声明synchronized方法。
synchronized 方法锁控制对类成员变量的访问:
每个类实例对应一把锁
每个synchronized方法都必须获得调用该方法的类实例的”锁“方能执行,否则所属线程阻塞。
对象锁
当一个对象中有synchronized method 或synchronized block 的时候,调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。
如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁)
类锁
类锁是锁住整个类,当有多个线程来声明这个类的对象时候将会被阻塞,直到拥有这个类锁的对象呗销毁或者主动释放了类锁,这个时候在被阻塞的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住
synchronized 和lock的区别
简单讲synchronized是JAVA的关键字,而lock是concurrent提供的工具类
区别:
1、 lock是可中断锁,而synchronized 不是可中断锁
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断,如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
2、 synchronized会自动释放,而lock需要代码释放
synchronized是在JVM层面上实现的,lock是通过代码实现的,JVM会自动释放锁定(代码执行完成或者出现异常),但是使用Lock则不行,要保证锁定一定会被释放,就必须将unLock()放到finally{}中。
我们来实际使用下lock
lock
public interface Lock {
void lock();//上锁
void lockInterruptibly() throws InterruptedException;//中断等待的线程
boolean tryLock();// 尝试获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//设置获取锁的超时时间
void unlock();//释放锁
Condition newCondition();//线程通讯
lock的实现类
我们分别来测试下
写一个 线程类
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class threadtTask implements Runnable{
private File file=null;
public threadtTask(File file ) {
this.file = file;
}
@Override
public void run() {
readMethod();
}
private synchronized void readMethod(){
byte[] buf = new byte[(int) file.length()];
InputStream reader ;
try {
reader =new FileInputStream(file);
reader.read(buf);
System.out.println("我是线程"+Thread.currentThread().getName()+" 读取到:"+new String(buf));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
System.out.println("我是线程"+Thread.currentThread().getName()+" 释放锁");
}
}
}
一个测试类
package lockTest;
import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class test {
public static void main(String[] args) {
File file = new File("D:/test/local/file.txt");
threadtTask task = new threadtTask(file);
Thread thread = new Thread(task," 线程1 ");
Thread thread2 = new Thread(task," 线程2 ");
Thread thread3 = new Thread(task," 线程3 ");
Thread thread4 = new Thread(task," 线程4 ");
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
}
上述代码使用的是synchronized同步锁
结果是:
我是读线程 线程1 读取到:test
我是读线程 线程1 释放锁
我是读线程 线程2 读取到:test
我是读线程 线程2 释放锁
我是读线程 线程3 读取到:test
我是读线程 线程3 释放锁
我是读线程 线程4 读取到:test
我是读线程 线程4 释放锁
需要等获得锁的线程释放锁,其他线程才可以获得锁
使用 锁ReentrantLock
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class threadtTask implements Runnable{
private File file=null;
private Lock lock = new ReentrantLock();
public threadtTask(File file ) {
this.file = file;
}
@Override
public void run() {
readMethod();
}
private void readMethod(){
lock.lock();
byte[] buf = new byte[(int) file.length()];
InputStream reader ;
try {
reader =new FileInputStream(file);
reader.read(buf);
System.out.println("我是线程"+Thread.currentThread().getName()+" 读取到:"+new String(buf));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
System.out.println("我是线程"+Thread.currentThread().getName()+" 释放锁");
lock.unlock();
}
}
}
运行结果是
我是读线程 线程1 读取到:test
我是读线程 线程1 释放锁
我是读线程 线程3 读取到:test
我是读线程 线程3 释放锁
我是读线程 线程2 读取到:test
我是读线程 线程2 释放锁
我是读线程 线程4 读取到:test
我是读线程 线程4 释放锁
需要等获得锁的线程释放锁才,其他线程才可以获得锁,证明lock也可以做到线程同步,保证线程安全。
我们老看lock另外两个实现类 ReadLock、WriteLock
代码与上段类似不重复写,写一下不同的地方
读取锁
private ReentrantReadWriteLock wLock = new ReentrantReadWriteLock();//新建对象
wLock.readLock().lock();//上读取锁
wLock.readLock().unlock();//释放锁
运行结果:其他线程不需要等待释放锁,即可以多线程共享资源。
我是读线程 线程4 读取到:test
我是读线程 线程1 读取到:test
我是读线程 线程2 读取到:test
我是读线程 线程2 释放锁
我是读线程 线程3 读取到:test
我是读线程 线程1 释放锁
我是读线程 线程4 释放锁
我是读线程 线程3 释放锁
写锁
private ReentrantReadWriteLock wLock = new ReentrantReadWriteLock();//新建对象
wLock.writeLock().lock();//上读取锁
wLock.writeLock().unlock();//释放锁
运行结果:需要等获得锁的线程释放锁,其他线程才可以获得锁。
我是读线程 线程1 读取到:test
我是读线程 线程1 释放锁
我是读线程 线程2 读取到:test
我是读线程 线程2 释放锁
我是读线程 线程3 读取到:test
我是读线程 线程3 释放锁
我是读线程 线程4 读取到:test
我是读线程 线程4 释放锁
上述为lock的基本用法
点这里
我们注意到配合synchronized锁,有wait/notify/notifyAll方法实现线程通信
而lock有new Condition属性,提供了await()/signal()/signalAll()来实现,而且可以实现对应线程的通信,而不是所有线程。