线程安全
卖票案例:
- 定义一个类Ticket实现Runnable接口,定义成员变量:privite int ticketCount=100;
- 在Ticket类中重写run()方法实现卖票,代码步骤如下:
- 判断票数大于0,就卖票,并告知是哪个窗口卖的。
- 票数-1
- 卖光,线程截至。
- 定义测试类TicketDemo,main方法代码步骤如下:
- 创建Ticket类的对象
- 创建三个Thread类的对象,把Ticket对象作为构造参数,并给出对应的窗口名称。
- 启动线程
实际生活中卖票需要时间,所以出售一张票时,需要一点时间延迟,用sleep()方法实现。
卖票出现问题:相同的票卖了多次;出现了负数票。
卖票安全问题解决:
为什么出现问题?--多线程操作数据
如何解决?--让程序没有安全问题的环境
怎么实现?--把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
同步代码块解决。
同步代码块:
格式:synchronized(任意对象){
多条语句操作共享数据的代码;
}
默认情况是打开的,只要有一个线程进去执行代码,锁就会关闭。
单线程执行完毕,锁才会自动打开。
好处和弊端:
好处:解决多线程数据安全问题
弊端:线程很多时,每个线程都会判断同步上的锁,很耗费资源,降低程序运行效率。
public class Ticket implements Runnable {
//票数
private int ticket = 100;
private Object obj=new Object();
@Override
public void run() {
while (true) {
//多个线程必须使用同一把锁
synchronized (obj){
if (ticket <= 0) {
//卖完了
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "卖出1张,剩余票数:" + ticket);
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
Thread thread3 = new Thread(ticket);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
同步方法
一、同步方法:就是把synchronized关键字加到方法上。
格式:
修饰符 synchronized返回值类型 方法名(方法参数){ }
同步代码块与同步方法的区别:
同步代码块可以锁住指定代码,同步方法是锁住方法中的所有代码。
同步代码块可以指定锁对象,同步方法不能指定锁对象。
同步方法的锁对象是:this
public class MyRunnable 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) {
break;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
}
}
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
}
}
private synchronized 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) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread1.start();
thread2.start();
}
}
二、同步静态方法:就是把synchronized关键字加到静态方法上
格式:
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法的锁对象是:类名.class
package com.itheima.threaddemo11;
public class MyRunnable implements Runnable {
private static 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 (MyRunnable.class) {
if (ticketCount == 0) {
break;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
}
}
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
}
}
private static synchronized 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) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread1.start();
thread2.start();
}
}
lock锁
lock实现提供比使用synchronized方法和语句更广泛的锁定操作。lock中提供了获得锁和释放锁。
void lock():获得锁
void lock():释放锁
lock是接口不能实例化,需要通过实现类ReentantLock实例化。
ReentrantLock的构造方法:
ReentrantLock():创建一个ReentrantLock的实例。
package com.itheima.threaddemo12;
import java.util.concurrent.locks.ReentrantLock;
/**
* 定义一个类Ticket实现Runnable接口,定义成员变量:privite int ticketCount=100;
* 在Ticket类中重写run()方法实现卖票,代码步骤如下:
* 判断票数大于0,就卖票,并告知是哪个窗口卖的。
* 票数-1
* 卖光,线程截至。
* 定义测试类TicketDemo,main方法代码步骤如下:
* 创建Ticket类的对象
* 创建三个Thread类的对象,把Ticket对象作为构造参数,并给出对应的窗口名称。
* 启动线程
*/
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) {
//多个线程必须使用同一把锁
//synchronized (obj){
try {
lock.lock();
if (ticket <= 0) {
//卖完了
break;
} else {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "卖出1张,剩余票数:" + ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//一般放到finally中保证锁释放
}
}
}
}
package com.itheima.threaddemo12;
public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
Thread thread3 = new Thread(ticket);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
死锁
指两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法往前执行。
解决方法:不要写锁嵌套
package com.itheima.threaddemo13;
public class Demo {
public static void main(String[] args) {
Object objA=new Object();
Object objB=new Object();
new Thread(()->{
while (true){
synchronized (objA){
synchronized (objB) {
System.out.println("小康同学在走路");
}
}
}
}).start();
new Thread(()->{
while (true){
synchronized (objB){
synchronized (objA) {
System.out.println("小薇同学在走路");
}
}
}
}).start();
}
}

浙公网安备 33010602011771号