JAVA锁的问题

今天我在学习集合框架想到一个问题,发现自己对锁不太了解

起因:
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 不是可中断锁

扫描二维码关注公众号,回复: 3969104 查看本文章

线程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()来实现,而且可以实现对应线程的通信,而不是所有线程。

猜你喜欢

转载自blog.csdn.net/qq_31941773/article/details/83384281