1.线程安全问题:
需求:模拟火车票100张 售票窗口
分析:
1.售票窗口使用线程来模拟
2.开4个窗口同时卖100张票
3.4个窗口卖票的任务是一样
public class MyRunnable implements Runnable {
int tickets = 100;
@Override
public void run() {
while (true){
if (tickets < 1){
break;
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
tickets--;
}
}
}
public class Tests {
public static void main(String[] args) {
/*
* 需求:模拟火车票100张 售票窗口
*
* 分析:
* 1.售票窗口使用线程来模拟
* 2.开4个窗口同时卖100张票
* 3.4个窗口卖票的任务是一样
* */
//创建任务对象
MyRunnable mr = new MyRunnable();
//创建四个窗口
Thread t1 = new Thread(mr, "窗口1");
Thread t2 = new Thread(mr, "窗口2");
Thread t3 = new Thread(mr, "窗口3");
Thread t4 = new Thread(mr, "窗口4");
// 启动线程,执行程序
t1.start();
t2.start();
t3.start();
t4.start();
}
}
程序出现了几个问题
1. 四个窗口输出了相同的票数
2. 遗漏票
3. 负数票
2.同步代码块:
synchronized 关键字
synchronized 关键字:表示【同步】的,他可以对 多行代码 进行 同步
将多行代码当成一个完整的整体,一个线程如果进入到这个代码块中,会全部执行完毕
执行结束后,其他线程才会执行
这样,即可保证这多行代码昨晚完整的整体,被一个线程完整的执行完毕
synchronized 被称之为“重量级的锁” 同时呢,也被部分程序员称之为【悲观锁】 --- 效率比较低
synchronized : 有几种使用方法
- 同步代码块【常用的】
- 同步方法【 常用的】
使用场景:
当我们使用多个线程访问同一个资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题
以上,我们多线程并发访问一个资源的时候容易出现安全性问题,也就是解决重复票或遗漏票的问题,Java提供了【同步机制】synchronized来处理
同步锁:synchronized
synchronized关键字可以用于方法中的某个区块中,表示支队这个区块的资源实行【互斥访问】
格式:
synchronized(锁对象){
需要同步操作的代码
}
锁对象:
1. 语法上,锁对象可以是任意类的对象
2. 多条线程想要实现同步,必须锁对象一致
public class MyRunnable implements Runnable {
//共享的变量
int tickets = 100;
@Override
public void run() {
while (true){
// 加锁
synchronized ("heihei") {
if (tickets < 1) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":正在售出第" + tickets + "张票");
tickets--;
}
// 解锁
}
}
public class Tests {
public static void main(String[] args) {
//创建任务对象
MyRunnable mr = new MyRunnable();
//创建四个窗口
Thread t1 = new Thread(mr, "窗口1");
Thread t2 = new Thread(mr, "窗口2");
Thread t3 = new Thread(mr, "窗口3");
Thread t4 = new Thread(mr, "窗口4");
// 启动线程,执行程序
t1.start();
t2.start();
t3.start();
t4.start();
}
}
3.同步方法:
概述: 使用synchronized修饰的方法,叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外面等着
格式:
权限修饰符 synchronized 返回值类型 方法名名称(参数列表){
}
锁对象:
非静态同步方法:锁对象是this
静态同步方法: 锁对象是该方法所在类的字节码对象(类名.class)
public class MyRunnable implements Runnable {
// 共享变量
int ticksts = 100;
@Override
public void run() {
while (true){
if (sellTickets()) break;
}
}
// synchronized修饰的就是同步方法 【非静态同步方法】
private synchronized boolean sellTickets() {
if (ticksts < 1){
return true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+ticksts+"张票");
ticksts--;
return false;
}
}
public class Tests {
public static void main(String[] args) {
//创建任务对象
MyRunnable mr = new MyRunnable();
//创建四个窗口
Thread t1 = new Thread(mr, "窗口1");
Thread t2 = new Thread(mr, "窗口2");
Thread t3 = new Thread(mr, "窗口3");
Thread t4 = new Thread(mr, "窗口4");
// 启动线程,执行程序
t1.start();
t2.start();
t3.start();
t4.start();
}
}
4.同步方法的锁对象
在日常开发中,一条线程使用的是同步代码块,一条线程使用的是同步方法,但这两条线程需要同步的相关业务
实现这个学术,同步代码块和同步方法的锁对象就必须一致
而同步方法的锁对象是默认的,所以必须清楚同步方法的锁对象
同步锁是谁?
对与非static方法,锁对象就是this
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
public class Demo {
public synchronized void method(){
System.out.println(Thread.currentThread().getName()+"打开厕所门");
System.out.println(Thread.currentThread().getName()+"关闭厕所门");
// 线程休眠一会儿 表示 ing...
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开门洗手走人");
}
}
public class Tests {
public static void main(String[] args) {
Demo demo = new Demo();
//张三上厕所
new Thread(new Runnable() {
@Override
public void run() {
demo.method();
}
},"张三").start();
//李四上厕所
new Thread(new Runnable() {
@Override
public void run() {
demo.method();
}
},"李四").start();
}
}
5.Lock锁
java.util.concurrent.locks.Lock 接口
Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作
同步代码块和同步方法具有的功能 Lock都有,除此之外,更加强大,更加面向对象
Lock锁 也称之为 同步锁。加锁和释放锁
- public void lock() 加同步锁
- public void unlock() 释放锁
public class MyRunnable implements Runnable{
//共享的变量
int tickets = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// 加锁
lock.lock();
if (tickets < 1){
lock.unlock();
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--;
// 释放锁
lock.unlock();
}
}
}
public class Tests {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"窗口1");
Thread t2 = new Thread(mr,"窗口2");
Thread t3 = new Thread(mr,"窗口3");
Thread t4 = new Thread(mr,"窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
System.out.println("程序结束");
}
}