Lock
Lock是一个接口,用来实现线程同步的,功能与synchronized一样。
Lock使用频率最高的实现类是ReentrantLock(重入锁),可以重复上锁
在实际工作中,使用如下方式进行开发:实现资源和Runnable接口的解耦合
package Thread.Lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02Test {
public static void main(String[] args) {
Account account = new Account();
new Thread(()->{
account.count();
},"A").start();
new Thread(()->{
account.count();
},"B").start();
}
}
class Account {
private int num;
private Lock lock= new ReentrantLock();
public void count() {
// 上锁
lock.lock();
num++;
System.out.println(Thread.currentThread().getName()+"是第"+num+"位访客");
//解锁
lock.unlock();
}
}
重入锁
JUC —顾名思义勾优C
java.util.concurrent
java并发编程工具包,Java官方提供的一套专门用来处理并发编程的工具集合(接口+类)
并发:单核cpu,多个线程同时“运行”,实际是交替执行,只不过速度太快,看起来是同时执行,想象一下这样的场景,两个厨师一口锅
并行:多核cpu,真正的多个线程同时执行,想象一下这样的场景:两个厨师两口锅
重入锁是JUC使用频率非常高的一个类ReentrantLock
ReentrantLock就是对synchronized的升级,目的也是为了实现线程同步。
两者之间的区别:
-
ReentrantLock是一个类,synchronize是一个关键字
-
ReentranLock是jdk实现,synchronize是jvm实现
-
synchronize可以实现自动释放锁,ReentrantLock需要手动释放锁
ReentrantLock是lock接口的实现类。
公平锁和非公平锁的区别:
公平锁: 线程同步时,多个线程排队,依次执行
非公平锁:线程同步时,可以插队
线程的实现有三种方式,主要实现方式是如下两种- 继承Thread
- 实现Runnable (实现Runnable接口的耦合度更低)
注意
package Thread.Lock;
import java.util.concurrent.TimeUnit;
public class Demo03Test {
public static void main(String[] args) {
Count count = new Count();
new Thread(()->{
count.count();
},"A").start();
new Thread(()->{
count.count();
},"B").start();
}
}
class Count{
private static Integer num=0;
private static Integer id = 0;
public void count(){
/**
* 不能锁num,因为每次num的值会发生变化,因此
* 导致每次锁的不是同一个对象,因此就锁不住
*/
synchronized (id){
num++;
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"是当前第"+num+"位访客");
}
}
}
如果锁定num不能同步,锁定id可以同步,原因是什么?
synchronized 必须锁定唯一的元素才可以实现同步,但是num的值每次都在变,所以num所指向的引用一直在变,所以不是唯一的元素,肯定无法实现同步,但是id的值永远不变,所以是唯一的元素,可以实现同步
ReentrantLock
package Thread.Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Demo05Test {
public static void main(String[] args) {
Account3 account3 = new Account3();
new Thread(()->{
account3.count();
}).start();
new Thread(()->{
account3.count();
}).start();
}
}
class Account3{
private static int num;
private ReentrantLock reentrantLock = new ReentrantLock();
public void count(){
// 上锁
reentrantLock.lock();
num++;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"是第"+num+"位访问的");
//释放锁
reentrantLock.unlock();
}
}
- Lock上锁和解锁都需要开发者手动完成
- 可以重复上锁,上几把锁就需要解几把锁
ReentranLock除了可以重入之外,还有一个可以中断的特点,可中断是指某个线程在等待获取锁的过程中可以主动终止线程
package Thread.Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Demo06Test {
public static void main(String[] args) {
StopLock stopLock = new StopLock();
Thread t1= new Thread(()->{
stopLock.service();
},"A");
Thread t2=new Thread(()->{
stopLock.service();
},"B");
t1.start();
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
t2.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class StopLock{
private ReentrantLock reentrantLock = new ReentrantLock();
public void service(){
// 可执行中断的锁
try {
reentrantLock.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
reentrantLock.unlock();
}
}
}
生产者消费者模式
在一个生产环境中,生产者和消费者在同一时间段内共享同一块缓冲区,生产者负责向缓冲区添加数据,消费者负责向缓冲区取出数据
汉堡类
public class Hamburger {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Hamburger(int id) {
this.id = id;
}
@Override
public String toString() {
return "Hamburger{" +
"id=" + id +
'}';
}
}
生产者
/**
* 生产者生产汉堡
*/
public class Producer {
private Container container = null;
public Producer(Container container){
this.container=container;
}
public void product(){
for (int i = 0; i <30 ; i++) {
Hamburger hamburger = new Hamburger(i);
// 将汉堡装进容器,紧接着继续生产
this.container.push(hamburger);
// 生产汉堡需要一定的时间,因此此处模拟生产时间。休眠一秒钟
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者
/**
* 消费者消费汉堡
*/
public class Consumer {
private Container container;
public Consumer(Container container){
this.container=container;
}
public void consum() {
for (int i = 0; i < 30; i++) {
// 消费汉堡
this.container.pop();
// 吃汉堡需要时间,因此此处模拟休眠一秒钟
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
/**
* 测试生产者生产汉堡
* 消费者消费汉堡
*/
public class Test {
public static void main(String[] args) {
Container container = new Container();
Producer producer = new Producer(container);
Consumer consumer = new Consumer(container);
new Thread(()->{
producer.product();
}).start();
new Thread(()->{
consumer.consum();
}).start();
}
}