线程安全

一、安全问题
假如一个电影院有一部电影100张票:

  1. 只有一个窗口卖1-100的票(单线程):不会出现线程问题。
  2. 有三个窗口卖不同范围的票(多线程):不会出现线程问题,没有访问共享数据,不会产生问题。
  3. 有三个窗口卖的都是1-100的票(多线程):会出现线程访问共享数据–>线程安全问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//子类!!!!!
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源:
private int ticket=100;
//设置线程任务:卖票
@Override
public void run() {
while(true)
{
//先判断票是否存在
if(ticket>0)
{
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//票存在,卖票 ticket--;
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}

//测试类!!!!!!!!!!!!!!!!!
/*
模拟卖票案例
创建3个线程,同时开始,对共享的票进行出售
*/
public class ShiXian {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run=new RunnableImpl();

//三个线程争夺CPU
Thread t0=new Thread(run);//抢夺到CPU的执行权-进入到run方法之后执行if语句就会睡眠,失去cpu的执行权
Thread t1=new Thread(run);
Thread t2=new Thread(run);

//调用start()方法开启多线程
t0.start();
t1.start();
t2.start();
}
}

代码结果如下:
Thread-1–>正在卖第100张票
Thread-2–>正在卖第100张票
Thread-0–>正在卖第100张票

Thread-1–>正在卖第3张票
Thread-0–>正在卖第3张票
Thread-2–>正在卖第1张票
Thread-1–>正在卖第0张票
Thread-0–>正在卖第1张票
(出现了同时卖出去票的问题!!!!!!)


二、线程同步(解决方案)

  1. 同步代码块
  2. 同步方法
  3. 锁机制(Lock接口)
  • 同步代码块(synchronized):
1
2
3
 synchronized(同步锁){
需要同步操作的代码
}

注意:

  1. 锁对象可以是任意的对象
  2. 保证多个线程使用的是同一个锁对象
  3. 锁对象的作用:
    把同步代码块锁住:只让一个线程在同步代码块中执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源:
private int ticket=100;

//创建一个锁对象
Object obj=new Object();

//设置线程任务:卖票
@Override
public void run() {
while(true)
{

synchronized (obj) //synchronized(锁对象)
{

//还是原来的那些if语句和sleep语句
}

}
}
}

代码结果如下:
Thread-0–>正在卖第100张票
Thread-2–>正在卖第99张票
Thread-2–>正在卖第98张票
…(省略)
Thread-2–>正在卖第4张票
Thread-2–>正在卖第3张票
Thread-2–>正在卖第2张票
Thread-2–>正在卖第1张票


  • 同步方法
  1. 访问了共享数据的代码–>方法
  2. 方法上+synchronized修饰符

格式:定义方法的格式

1
修饰符 synchronized 返回值类型 方法名(参数列表){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package duoxianchegn;

public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源:
private int ticket=100;

//设置线程任务:卖票
@Override
public void run() {
while(true)
{
payTicket(); //调用这个方法!

}
}

//提出来一个payTicket()方法用于把共享的代码锁起来
public synchronized void payTicket() {
//先判断票是否存在
if(ticket>0)
{
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//票存在,卖票 ticket--;
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}

}
}

代码结果如下:
Thread-0–>正在卖第100张票
Thread-2–>正在卖第99张票
Thread-2–>正在卖第98张票
…(省略)
Thread-2–>正在卖第4张票
Thread-2–>正在卖第3张票
Thread-2–>正在卖第2张票
Thread-2–>正在卖第1张票


  • 锁机制(Lock锁)
    java.util.concurrent.locks.lock接口

  • lock接口(比synchronized更好)
     1. 使用获取锁void lock()/释放锁void unlock()方法。
     2. 实现类ReentrantLock implements Lock接口

  • 使用步骤:

  1. 在成员位置–>ReentrantLock对象
  2. 在可能会出现安全问题的代码调用Lock方法获取锁
  3. 在可能会出现安全问题的代码调用Lock方法释放锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源:
private int ticket=100;
//定义一个对象
Lock l=new ReentrantLock();

//设置线程任务:卖票
@Override
public void run() {
while(true)
{
l.lock();//加一个锁
//先判断票是否存在
if(ticket>0)
{
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//票存在,卖票 ticket--;
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
l.unlock();//释放一个锁
}
}
}

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
,