多线程买火车票 如果不加锁的话顺序会乱而且会多卖相同的票
synchronized 锁实现买票
public class SaleTicketDemo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
ticket.sale();
}
}, "C").start();
}
}
class Ticket {
private int number = 20;
//synchronized 本质队列锁
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
}
}
}
输出:
A卖出了20票,剩余19 A卖出了19票,剩余18 A卖出了18票,剩余17 A卖出了17票,剩余16 B卖出了16票,剩余15 B卖出了15票,剩余14 B卖出了14票,剩余13 B卖出了13票,剩余12 B卖出了12票,剩余11 B卖出了11票,剩余10 B卖出了10票,剩余9 B卖出了9票,剩余8 B卖出了8票,剩余7 B卖出了7票,剩余6 A卖出了6票,剩余5 A卖出了5票,剩余4 A卖出了4票,剩余3 A卖出了3票,剩余2 A卖出了2票,剩余1 A卖出了1票,剩余0
lock锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
// lambda 表达式 (参数)->{代码}
new Thread(() -> {
for (int i = 0; i < 10; i++) ticket.sale();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) ticket.sale();
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) ticket.sale();
}, "C").start();
}
}
// lock锁
//主要步骤
class Ticket2 {
private int number = 20;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock(); //加锁
try {
//业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
}
输出:
A卖出了20票,剩余19 A卖出了19票,剩余18 A卖出了18票,剩余17 A卖出了17票,剩余16 A卖出了16票,剩余15 A卖出了15票,剩余14 A卖出了14票,剩余13 A卖出了13票,剩余12 A卖出了12票,剩余11 C卖出了11票,剩余10 C卖出了10票,剩余9 C卖出了9票,剩余8 C卖出了8票,剩余7 C卖出了7票,剩余6 C卖出了6票,剩余5 C卖出了5票,剩余4 C卖出了4票,剩余3 C卖出了3票,剩余2 C卖出了2票,剩余1 A卖出了1票,剩余0
发现两种锁都达到了目的那他们有什么区别呢:
1.synchronized 是java内置的关键字,Lock是一个类
2.synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3.synchronized 会自动释放锁,Lock 必须要手动释放,如果不释放则会出现死锁
4.synchronized 如果同时有两个线程执行A 、B如果A获得锁处于阻塞状态B线程就会一致等下去
Lock就不一定如果A线程处于阻塞 B线程肯能会执行;
5.synchronized 可重入锁,不可以中断相对于来说非公平(如果A线程需要2分钟而B线程需要两秒如果A没有执行完毕则B会一致等待下去),Lock :可重入锁,可以判断锁,非公平(可以自己设置)
6.synchronized 适合锁少量的代码同步问题。Lock 适合锁大量的同步代码
生产者消费者问题
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 判断等待,业务,通知
class Data {
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程+1执行完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程-1执行完毕
this.notifyAll();
}
}
输出:
A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0
如果不止两个线程会出现什么问题呢:
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Data {
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程+1执行完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程-1执行完毕
this.notifyAll();
}
}
输出:
A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 C=>1 B=>0 A=>1 B=>0 C=>1 B=>0 A=>1 B=>0 C=>1 D=>0 C=>1 B=>0 A=>1 B=>0 C=>1 D=>0 C=>1 B=>0 A=>1 C=>2 D=>1 D=>0 C=>1 A=>2 C=>3 D=>2 D=>1 D=>0 C=>1 A=>2 D=>1 D=>0 A=>1 D=>0
发现和两个线程 不一样 两个线程只有 01 0101 而这个出现了其他的 2 3
为什么会出现这种情况呢查看api java.long包中Object类会有这么一段话
虚假唤醒 那怎么解决呢
只需要把if 改为 while 即可解决问题
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Data {
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程+1执行完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程-1执行完毕
this.notifyAll();
}
}
输出:
A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 C=>1 D=>0 A=>1 B=>0 C=>1 D=>0 A=>1 B=>0 C=>1 D=>0 A=>1 B=>0 C=>1 D=>0 A=>1 B=>0 C=>1 D=>0 A=>1 B=>0 C=>1 D=>0 C=>1 D=>0 C=>1 D=>0 C=>1 D=>0 C=>1 D=>0
JUC版生产者消费者
在api Condition类中会有这么一段代码
代码实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await();//等待
// condition.signalAll();//唤醒
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程+1执行完毕
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// -1
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程 这个线程-1执行完毕
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出
A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 A=>1 D=>0 C=>1 B=>0 C=>1 D=>0 C=>1 D=>0 C=>1 D=>0
那能不能A执行完毕之后B执行,然后C执行,再D执行呢(A->B->C->D->A)?
那就要用到Condition 的精准通知和唤醒线程
代码测试:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printC();
}
}, "C").start();
}
}
// 资源类
class Data3 {
private Lock lock = new ReentrantLock();
private int number = 1;
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void printA() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知->
while (number != 1) {
//等待
conditionA.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAA");
//唤醒 指定线程
number = 2;
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知->
while (number != 2) {
//等待
conditionB.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBBB");
//唤醒 指定线程
number = 3;
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
// 业务,判断-> 执行-> 通知->
while (number != 3) {
//等待
conditionC.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCCC");
//唤醒 指定线程
number = 1;
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出:
A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC A=>AAAA B=>BBBB C=>CCCC
8锁现象
如何判断锁是谁
创建一个手机类里面有两个方法 发信息和打电话再创建一个测试类去测试
代码:
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
//发信息
public synchronized void sendSms() {
System.out.println("发信息");
}
//打电话
public synchronized void call() {
System.out.println("打电话");
}
}
输出:
发信息 打电话
那如果把发信息延迟3秒呢又是谁先执行呢
代码:
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
//发信息
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//打电话
public synchronized void call() {
System.out.println("打电话");
}
}
输出:
发信息
打电话
synchronized 锁的对象是方法的调用者 两个方法调用的是同一个锁,谁先拿到谁执行
那如果再phone类中加一个普通方法那又会是什么样的呢?
代码:
import java.util.concurrent.TimeUnit;
public class Test2{
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
class Phone2 {
//发信息
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//打电话
public synchronized void call() {
System.out.println("打电话");
}
//hello 这个没有锁
public void hello(){
System.out.println("hello");
}
}
输出:
hello
发信息
原因: hello这个方法没有锁,不是同步方法,不受锁的影响
两个对象。两个同步方法 ,发短信和打电话谁先打印呢
代码:
import java.util.concurrent.TimeUnit;
public class Test2{
public static void main(String[] args) {
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone2 {
//发信息
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//打电话
public synchronized void call() {
System.out.println("打电话");
}
//hello 这个没有锁
public void hello(){
System.out.println("hello");
}
}
输出:
打电话
发信息
这是为什么呢?
原因:创建的是两个对象拿到的是两把锁又因为sendSms方法延迟了3秒所以先打印打电话
那如果只有一个对象再把这两个方法变为静态的又会出现什么呢?
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
Phone3 phone = new Phone3();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone3 {
//发信息
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//打电话
public static synchronized void call() {
System.out.println("打电话");
}
}
输出:
发信息
打电话
加了static 变为静态方法 类一加载就有了,所有锁的是Class
如果还是这个条件变为两个对象呢?
代码:
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone3 {
//发信息
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
//打电话
public static synchronized void call() {
System.out.println("打电话");
}
}
输出:
发信息
打电话
这是因为两个对象的Class类模板只有一个,static,锁的是Class
那如果一个是静态的同步方法另一个是普通的同步方法又会发生什么呢?
代码:
import java.util.concurrent.TimeUnit;
public class Test4 {
public static void main(String[] args) {
Phone4 phone = new Phone4();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone4 {
// 静态的同步方法 锁的是Class 类模板
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发信息");
}
// 普通的同步方法 锁的是调用者
public synchronized void call() {
System.out.println("打电话");
}
}
输出:
打电话
发信息
如果变成两个对象呢
import java.util.concurrent.TimeUnit;
public class Test4 {
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
输出和上面的一样原因也一样



