2.0 锁(Lock)
-
从JDK 5.0开始,Java提供 了更强大的线程同步机制一通过显式定 义同步锁对象来实现同
步。同步锁使用Lock对象充当
-
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了
对共享资源的独占访问,每次只能有一个线程对L ock对象加锁,线程开始访问共享资源之前
应先获得l ock对象
-
ReentrantLock(可重入锁)实现了Lock ,它拥有与synchronized相同的并发性和内存语义,
在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
package com.gong.lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket).start();
new Thread(buyTicket).start();
new Thread(buyTicket).start();
}
}
class BuyTicket implements Runnable{
private int ticket = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
if (ticket > 0) {
Thread.sleep(1000);
System.out.println(ticket--);
}else{
break;
}
} catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
}
2.0.1synchronized与Lock的对比
-
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁, 出了作用域自动
释放
-
Lock只有代码块锁,synchronized有代码块锁和方法锁
-
使用Lock锁, JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多
的子类)
-
优先使用顺序:
- Lock >同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)
2.1 生产者消费者问题
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之
间相互依赖,互为条件.
-
对于生产者,没有生产产品之前,要通知消费者等待.而生产了产品之后,又
需要马_上通知消费者消费 -
对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品
以供消费. -
在生产者消费者问题中, 仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
-
java提供了几个方法(均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会
抛出异常IllegalMonitorStateException)解决现存之间的通信问题
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上搜用处于等待状态的线程,优先级高的线程优先调度 |
解决方法:
-
并发协作模型“生产者/消费者模式”–>管程法
- 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
- 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区
- 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
-
并发协作模型“生产者/消费者模式”–>信号灯法
管程法
package com.gong.lock;
//测试:并发协作模型“生产者/消费者模式”-->管程法
//生产者,消费者,产品,缓冲区
public class MonitorTest {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Productor productor = new Productor(container);
Consumer consumer = new Consumer(container);
productor.start();
consumer.start();
}
}
//生产者
class Productor extends Thread{
SynContainer container = new SynContainer();
public Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 1; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container = new SynContainer();
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+ container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
public synchronized void push(Chicken chicken){
//如果满了,通知消费者消费
if(count==chickens.length){
//生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满.丢入产品
chickens[count] = chicken;
count++;
//通知消费者消费
this.notify();
}
public synchronized Chicken pop(){
if(count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//进行消费
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notify();
return chicken;
}
}
信号灯法
package com.gong.lock;
public class SingalLight {
public static void main(String[] args) {
Tv tv = new Tv();
new Player(tv).start();
new Watcher(tv).start();
}
}
class Player extends Thread{
Tv tv = new Tv();
public Player(Tv tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0) {
tv.play("火影忍者");
}else{
tv.play("天线宝宝");
}
}
}
}
class Watcher extends Thread{
Tv tv = new Tv();
public Watcher(Tv tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
class Tv{
String voice;//节目
private boolean flag = true;
public synchronized void play(String voice){
if(!flag){
try {
this.wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了"+voice);
this.voice = voice;
this.notifyAll();//唤醒
this.flag =!this.flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了"+voice);
this.notifyAll();//唤醒
this.flag =!this.flag;
}
}
2.2 线程池
-
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
-
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁
创建销毁、实现重复利用。类似生活中的公共交通工具。
-
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理(…
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime: 线程没有任务时最多保持多长时间后会终止
package com.gong.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
推荐去看B站【狂神说Java】的视频https://space.bilibili.com/95256449?spm_id_from=333.788.b_765f7570696e666f.1