同步代码块
作用:
锁多条语句操作共享数据,可以使用代码同步块实现
格式:
synchronized (任意对象) {
多条语句操作共享数据的代码
}
- 默认情况是打开的,只要有一个线程进去了,锁就会关闭。(类似在多人宿舍只有一个厕所上大号的情况)
- 当线程序执行完了,锁才会打开,供下一个线程抢占。
同步的优缺点:
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
案例:
public class Ticket implements Runnable {
int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
//小括号内写的是锁对象,并且锁对象应要唯一
synchronized (obj){
if(ticket == 0){
System.out.println("票已售罄");
break;
}else{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+ "正在卖票,当前剩余" + ticket + "张");
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket tc = new Ticket();
Thread t1 = new Thread(tc);
t1.setName("窗口一");
t1.start();
Thread t2 = new Thread(tc);
t2.setName("窗口贰");
t2.start();
Thread t3 = new Thread(tc);
t3.setName("窗口叁");
t3.start();
}
}
同步方法
同步方法就是把synchronized关键字放在方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步代码块和同步方法的区别
- 同步代码块可以锁住指定代码,同步方法是锁住方法中的所有代码
- 同步代码块可以指定锁对象,同步方法不能指定锁对象
同步方法的锁对象是this.
public class MyThread implements Runnable {
private int ticketcount = 100;
@Override
public void run() {
while (true){
if("窗口一".equals(Thread.currentThread().getName())){
//使用同步方法
boolean result = synchronizedmethod();
if (result){
break;
}
}
if("窗口二".equals(Thread.currentThread().getName())){
//使用同步代码块
synchronized (this){
if(ticketcount == 0){
System.out.println("票已售罄");
break;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketcount--;
System.out.println(Thread.currentThread().getName()+ "正在卖票,当前剩余" + ticketcount + "张");
}
}
}
}
}
private boolean synchronizedmethod() {
if(ticketcount == 0){
return true;
}else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketcount--;
System.out.println(Thread.currentThread().getName()+ "正在卖票,当前剩余" + ticketcount + "张");
return false;
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t= new Thread(mt);
Thread t2= new Thread(mt);
t.setName("窗口一");
t2.setName("窗口二");
t.start();
t2.start();
}
}
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
- void lock0 : 获得锁
- void unlock0 : 释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
public class Ticket implements Runnable {
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//手动上锁
if (ticket == 0) {
System.out.println("票已售罄");
break;
} else {
Thread.sleep(200);
ticket--;
System.out.println(Thread.currentThread().getName() + "正在卖票,当前剩余" + ticket + "张");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//手动释放锁
}
}
}
}
死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
示例:
public static void main(String[] args) {
//lambda表达式开启多线程
Object oa = new Object();
Object ob = new Object();
new Thread(()->{
while (true){
synchronized (oa){
synchronized (ob){
System.out.println("我在行走");
}
}
}
}).start();
new Thread(()->{
while (true){
synchronized (ob){
synchronized (oa){
System.out.println("你也在上行走");
}
}
}
}).start();
}
生产者和消费者
小练习:
public class Desk {
//汉堡数量
public static int count = 10;
//桌上是否有汉堡
public static boolean exit = false;
//唯一的无法修改的锁对象
public static final Object lock = new Object();
}
public class Cooker extends Thread {
@Override
public void run() {
while (true){
synchronized (Desk.lock){
// 确认后台是否还有汉堡
if (Desk.count == 0){
//如果后台没有汉堡了,就退出循环
System.out.println("后台没有汉堡了,快被这个吃货吃完了");
break;
}else {
if(!Desk.exit){
//如果后台有汉堡,但桌上没有汉堡了,就开始做汉堡
System.out.println("厨师开始做汉堡");
Desk.exit=true;
//叫醒客人吃汉堡,唤醒这把锁上所有等待的线程
Desk.lock.notifyAll();
}else {
//如果后台有汉堡,桌上也有,就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Fooder extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
//判断是否还有汉堡,
if(Desk.count == 0){
//如果没汉堡,退出循环
break;
}else{
//如果还有汉堡
//确认桌上是否有汉堡存在
if(Desk.exit){
//如果有汉堡
System.out.println("客人开始吃汉堡");
Desk.count--;
Desk.exit = false;
//叫醒等待的生产者继续生产
Desk.lock.notifyAll();
}else{
//如果没有汉堡,就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
//创建线程对象
Cooker c = new Cooker();
Fooder f = new Fooder();
f.start();
c.start();
}
}