并发编程之多线程线程安全

展开

并发编程之多线程线程安全

为什么有线程安全问题?

线程安全解决办法

内置的锁

同步代码块synchronized

非静态同步方法

静态同步函数

多线程死锁

Threadlocal

Threadlocal API

ThreadLoca实现原理

多线程有三大特性

  1. 原子性

  2. 可见性

  3. 有序性

Java内存模型

Volatile

Volatile特性

volatile 性能:

Volatile与Synchronized区别

重排序

数据依赖性

as-if-serial语义

重排序对多线程的影响

为什么有线程安全问题?

当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

案例: 需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。

public class ThreadTrain implements Runnable {
private int trainCount = 100;

@Override

public void run() {

while (trainCount > 0) {

try {

Thread.sleep(50);

} catch (Exception e) {

}

sale();

}

}

public void sale() {

if (trainCount > 0) {

System.out.println(Thread.currentThread().getName() + “,出售第” + (100 - trainCount + 1) + “张票”);

trainCount–;

}

}

public static void main(String[] args) {

ThreadTrain threadTrain = new ThreadTrain();

Thread t1 = new Thread(threadTrain, “①号”);

Thread t2 = new Thread(threadTrain, “②号”);

t1.start();

t2.start();

}

}

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

运行结果: 一号窗口和二号窗口同时出售火车第九九张,部分火车票会重复出售。

结论发现,多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

线程安全解决办法

内置的锁

Java提供了一种内置的锁机制来支持原子性

每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁

内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁

内置锁使用synchronized关键字实现,synchronized关键字有两种用法:

1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象

2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活.

同步代码块synchronized

同步的前提:

1,必须要有两个或者两个以上的线程

2,必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行

好处:解决了多线程的安全问题

弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

public void sale() {

synchronized (this) {

if (trainCount > 0) {

System.out.println(Thread.currentThread().getName() + “,出售第” + (100 - trainCount + 1) + “张票”);

trainCount–;

}

}

}

1

2

3

4

5

6

7

8

非静态同步方法

在方法上修饰synchronized 称为同步方法

public synchronized void sale() {

if (trainCount > 0) {

System.out.println(Thread.currentThread().getName() + “,出售第” + (100 - trainCount + 1) + “张票”);

trainCount–;

}

}

1

2

3

4

5

6

非静态同步方法 = 同步代码块使用this锁 它们都是以当前对象作为锁

public synchronized void sale() {} = synchronized (this){}

this锁只用在非静态方法里面,静态方法是跟随类的,不跟随某个对象this。

证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。

class Thread009 implements Runnable {

private int trainCount = 100;

private Object oj = new Object();

public boolean flag = true;

public void run() {

if (flag) {

while (trainCount > 0) {

synchronized (this) {

try {

Thread.sleep(10);

} catch (Exception e) {

}

if (trainCount > 0) {

System.out.println(Thread.currentThread().getName() + “,” + “出售第” + (100 - trainCount + 1) + “票”);

trainCount–;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值