一 编写多线程的步骤
1.1 创建多线程的步骤
1.创建资源类,在资源类中创建属性和方法。
2.在资源类上操作方法
1.判断
2.干活
3.通知
3.创建多个线程,调用资源类的方法。
1.2 synchronized 同步锁
synchronized是Java的关键字,是一种同步锁,能够修饰 一个 对象,变量,方法,来控制这个所修饰的,被顺序的访问。
解决并发无非就是加锁,保证访问共享资源的操作原子性。例如加synchronized关键字。
1.3 lock锁
1.实现和synchronized同样的功能,比synchronized 操作使用更加灵活。
2.重入锁的使用格式:
// 创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
try {
//上锁
lock.lock();
//功能操作
...
}finally {
//解锁
lock.unlock();
}
1.4 lock与synchronized的区别与联系
1.synchronized是java内置的关键字,而lock不是内置,是一个类。
2.synchronized不需要手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
3.通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
4.Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候)
锁会出现死锁,需要在 finally 块中释放锁)
二 使用synchronized和lock的实现案例
2.1 需求描述
假设有30张票,需要3个人进行售卖,实现准确无异常。
2.2 假设1个窗口3个人方式进行售卖
2.2.1.逻辑图
2.2.2.代码实现
1.资源
package com.ljf.thread.juc.ticket.synch;
/**
* @ClassName: Resources
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/08/21 20:34:42
* @Version: V1.0
**/
public class Resources {
private static int ticket=100;
public synchronized void sell() throws InterruptedException {
if(ticket>0){
System.out.println("线程:"+Thread.currentThread().getName()+" 消费"+(ticket--)+"还剩余:"+(ticket));
Thread.sleep(1000);
}
}
}
2.窗口任务
package com.ljf.thread.juc.ticket.synch;
import com.ljf.thread.juc.ticket.synch2.Resource;
/**
* @ClassName: SellerTickets
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/08/21 20:35:26
* @Version: V1.0
**/
public class SellerTickets implements Runnable {
private Resource resource;
public SellerTickets(Resource r) {
this.resource=r;
}
@Override
public void run() {
while(Resource.ticket>0){
try {
resource.sell();
} catch (InterruptedException e) {
e.printStackTrace();
}
;
}
}
}
3.线程调用
package com.ljf.thread.juc.ticket.synch;
import com.ljf.thread.juc.ticket.synch2.Resource;
/**
* @ClassName: TestResources
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/08/21 20:41:50
* @Version: V1.0
**/
public class TestResources {
public static void main(String[] args) {
Resource resource=new Resource();
SellerTickets t= new SellerTickets(resource);
new Thread(t,"a").start();
new Thread(t,"b").start();
new Thread(t,"c").start();
}
}
效果如下:
2.3 假设3个窗口3个人方式进行售卖
2.3.1.逻辑图
2.3.2.代码实现
1.资源
package com.ljf.thread.juc.ticket.synch2;
/**
* @ClassName: Resource
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 16:38:09
* @Version: V1.0
**/
public class Resource {
public static int ticket=30;
public synchronized void sell() throws InterruptedException {
if(ticket>0){
System.out.println("线程:"+Thread.currentThread().getName()+" 消费第"+(ticket--)+" 还剩余:"+(ticket));
Thread.sleep(1000);
}
}
}
2.任务窗口
package com.ljf.thread.juc.ticket.synch2;
import com.ljf.thread.juc.ticket.synch2.Resource;
/**
* @ClassName: SubwayTickets
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 16:23:05
* @Version: V1.0
**/
public class SubwayTickets implements Runnable {
private Resource r;
public SubwayTickets(Resource r) {
this.r=r;
}
@Override
public void run() {
while(Resource.ticket>0){
try {
r.sell();
} catch (InterruptedException e) {
e.printStackTrace();
}
;
}
}
}
3.多线程调用
package com.ljf.thread.juc.ticket.synch2;
import com.ljf.thread.juc.ticket.synch2.Resource;
import com.ljf.thread.juc.ticket.synch2.SubwayTickets;
/**
* @ClassName: TestTicket
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 16:25:03
* @Version: V1.0
**/
public class TestTicket {
public static void main(String[] args) {
final Resource r = new Resource();
new Thread(new SubwayTickets(r),"线程A").start();
new Thread(new SubwayTickets(r),"线程B").start();
new Thread(new SubwayTickets(r),"线程C").start();
/**
Thread t1 = new Thread(new Runnable() {
@Override //具体创建线程的方法是new Thread(new Runnable({ 重写run方法},线程名)
public void run() {
for (int i = 0; i < 40; i++) {
r.sell();
}
}
}, "t1"
);
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override //具体创建线程的方法是new Thread(new Runnable({ 重写run方法},线程名)
public void run() {
for (int i = 0; i < 40; i++) {
r.sell();
}
}
}, "t2"
);
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override //具体创建线程的方法是new Thread(new Runnable({ 重写run方法},线程名)
public void run() {
for (int i = 0; i < 40; i++) {
r.sell();
}
}
}, "t3"
);
t3.start();
**/
}
}
4.结果调用
2.4 使用lock假设1个窗口3个人方式进行售卖
2.4.1 截图如下
1.资源类
package com.ljf.thread.juc.ticket.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName: ResourceTicket
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 17:18:35
* @Version: V1.0
**/
public class ResourceTicket {
public static int ticket=150;
private ReentrantLock lock=new ReentrantLock();
public void sell() throws InterruptedException {
lock.lock();;
try {
if (ticket > 0) {
System.out.println("线程:" + Thread.currentThread().getName() + " 消费" + (ticket--) + "还剩余:" + (ticket));
// Thread.sleep(100);
}
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
2.任务窗口
package com.ljf.thread.juc.ticket.lock;
/**
* @ClassName: TicketWindow
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 17:20:25
* @Version: V1.0
**/
public class TicketWindow implements Runnable{
private ResourceTicket resourceTicket;
public TicketWindow(ResourceTicket resourceTicket) {
this.resourceTicket=resourceTicket;
}
@Override
public void run() {
while(ResourceTicket.ticket>0){
try {
resourceTicket.sell();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.多线程执行
package com.ljf.thread.juc.ticket.lock;
/**
* @ClassName: TestTicketByLock
* @Description: TODO
* @Author: liujianfu
* @Date: 2022/07/18 17:22:16
* @Version: V1.0
**/
public class TestTicketByLock {
public static void main(String[] args) {
ResourceTicket resourceTicket=new ResourceTicket();
TicketWindow ticketWindow=new TicketWindow(resourceTicket);
new Thread(ticketWindow,"A").start();
new Thread(ticketWindow,"B").start();
new Thread(ticketWindow,"C").start();
}
}
效果图: