✨✨hello,愿意点进来的小伙伴们,你们好呐!
系列专栏:【JavaEE】
本篇内容:带你从0到1了解多线程基础
作者简介:一名现大二的三非编程小白,日复一日,仍需努力。
在多线程的编程中,一些比较敏感的数据不允许被多个线程同步访问,因此我们就要用同步访问技术来保证每一个数据在任何时刻,最多只有一个线程可以访问,来保证数据的安全。也就是 在每一时刻只有一个线程对该数据进行操作,其他线程只能等该线程访问完后再来访问。
Synchronized可以用在同步代码块和同步方法中。
1.同步代码块:
synchronized (this) {
}
2.同步方法:
public synchronized void run() {
}
同步代码块要如何理解呢?
就好像某个人要去排队核酸一样,当有人在核酸的时候,其他所有人都要等这个人核酸后(解锁)才有机会去核酸(抢锁)
我们来用Synchronized来解决售票问题体会一下同步锁
1.未使用同步锁
public class test {
public static void main(String[] args) {
SellTicket1 ticket = new SellTicket1();
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
Thread thread3 = new Thread(ticket);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket1 implements Runnable{
private static int count = 100;
@Override
public void run() {
while(true){
if(count < 0){
System.out.println("售票结束");
break;
}
System.out.println(Thread.currentThread().getName()
+ "售出一张票,剩余票数 : " + count--);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我们会发现在售票的时候会出现超卖的现象,有不同线程售同一张票的情况。这时候我们就要使用Synchronized锁来对售票系统进行同步。
=2.使用同步锁==
public class test {
public static void main(String[] args) {
SellTicket1 ticket = new SellTicket1();
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
Thread thread3 = new Thread(ticket);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket1 implements Runnable{
private static int count = 100;
@Override
public void run() {
while(true){
synchronized (this) {
if(count < 0){
System.out.println("售票结束");
break;
}
System.out.println(Thread.currentThread().getName()
+ "售出一张票,剩余票数 : " + count--);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
当适用synchronized 后就不在再会有超卖的现象了。
接下来我们来了解synchronized 的一些细节
1.Java语言中,引入了互斥锁来保证数据操作的完整性。
2.每一个对象都对应一个互斥锁的标记,这个标记再保证某一时刻,只能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表面该对象在任何一时刻只能有一个线程来访问。
4.同步会让程序的执行效率降低。
5.同步方法(非静态的)锁可以是用this,也是可以是其他对象(但是必须是同一个对象)。
6.同步方法(静态的)的锁为当前类本身。
7.如果同步方法使用start,默认锁对象是当前类.class
当我们synchronized使用不当的时候,就有可能会发生一个情况— 线程死锁
就相对于:
妈妈:你先玩写作业,然后再玩手机。
小明:你先让我玩手机,然后再写作业 。
这样子就会一直卡住,然后陷入其中,无法脱离。
下面我们来用代码来解释一下死锁现象:(代码有详细解释)
public class test2 {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}
class DeadLockDemo extends Thread{
static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + " 进入 1");
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入 3");
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 4");
}
}
}
}
}
会释放锁的操作:
1.同步代码块执行结束
2. 同步方法中遇到break,return
3.同步方法中遇到了Error或者Exception
4. 同步方法中执行了线程对象的wait(),当前线程暂停,并释放锁。
不会释放锁的操作:
1.在同步代码块中调用,Thread.sleep(),只会暂停当前的线程,不会释放锁。
2. 其他线程调用了suspend()后会将线程挂起。也不会释放锁。