JavaSE3(4/26)

目录

1.线程的状态

2.线程安全问题

3.synchronized的具体用法

4.


1.线程的状态

首先明白进程的状态:就绪或者阻塞

上述说的就绪和阻塞其实是针对系统中的线程状态(PCB)

Java中对于Thread类中的线程的状态进行了进一步的细化

NEW: Thread对象有了,但是线程还没有被执行

TERMINATE:线程无了,但是Thread对象还在

Time_Waiting   sleep(1000) 和join(1000) 导致的有限时间的阻塞

Waiting    等待唤醒导致的阻塞

Blocked 加锁导致的阻塞synchronized

2.线程安全问题

什么是线程不安全?

由于操作系统调度线程是随机的(抢占式执行)

所以由于随机性的调度线程可能会出现一些bug,这些我们成为线程不安全

线程安全不安全指的是有没有bug

上述是线程不安全的一个例子。

因为count++执行有三部(3个CPU指令),

1.获取count的值到寄存器

2.在寄存器中的值加1

3.寄存器中的值返回count的值

而如果不加锁,多个线程是抢占式执行的,他们同时可能都抢到了同样的一个值的count,假若说为100,那么他们加1后都是101,count进行了两个不同线程的一次++,本应该+2,却+了1,这就是线程不安全,给increase函数上锁,同时只能一个线程进入这个函数,就会解决这个问题

加锁解决线程安全问题

Object lock =new Object()

synchronized(lock){

//执行的加锁代码

}

加锁的机制,只有一把锁,一个线程加锁了后面的线程再加锁就会阻塞,直到加锁的那个线程执行完毕unlock。

通过阻塞,可以使一个乱序的并发变成一个串行操作,串行和单线程没啥区别了。

并发性越高,速度越快,但同时可能会导致线程不安全的bug

加了锁之后并发程度降低,速度变慢了。但是这是必要的牺牲。

多线程仍然是有非常大的意义的。实际开发中,一个线程会有许多的任务,他们的少部分是加锁的,但是剩下的大部分都是并发的,比串行还是快的多的。

        


加锁的方式

Java中加锁的方式有很多种,最多的就是synchronized关键字

1.直接给方法前面加synchronized

当一个线程加锁了之后,其他线程再加锁,就会处于阻塞状态(Blocked状态)阻塞一直持续到占用锁的线程Unlock

2.new个方法 synchronized(lock){代码体}


什么时候会线程不安全?

并不是所有的线程都要加锁,加锁了之后就和串行执行一样了(多线程的优势就形同虚设了)

线程不安全的原因

1.线程是抢占式随机执行的线程是抢占式执行同一个资源

2.多个线程对同一个变量进行修改操作 ,多个线程对不同变量修改没事,多个线程对同一个变量读也没事

3.针对变量的操作不是原子的~(也就是说虽然只是一个行代码,但是cpu执行分为好几个步骤 如count++)

4.内存可见性:线程一对内存中的数据进行修改的时候,线程2可能感受不到,这就会导致线程不安全。

编译器对程序的优化可能会导致线程误判(线程2感受不到线程1的修改)

解决内存可见性方法:

1.synchronized加锁,被它加锁,编译器不会假设一个变量不会改变。

synchronized既保证了内存可见性,又保证了原子性

2.volatile 关键字

volatile关键字只保证了内存可见性,和原子性无关。

使用volatile 关键字对变量进行修饰,就是禁止编译器进行优化,每次读取数据只从  内从中去读取。

补充:编译器优化(很玄学),如果读的操作,编译器发现每次从内存中读的都是一个数据不变,重复很多很多次,它就会从寄存器中去读取数据(为了更快),但是后面一旦这个内存中的数据被修改,编译器感受不到,就导致了线程不安全。

补充:原子性:要么全部一起执行完毕,要么一个也不执行。count++这个不具备原子性,它的三部可以被不同的线程打乱         

加锁操作就会把多个机器指令打包成一个原子的操作(一起执行的操作)

5.指令重排序,也会影响到线程安全问题,指令重排序也是编译器优化的一种操作

(就是对代码顺序的重新排序,使之更加高效(保证逻辑不变得前提下,再去调整顺序).一般不影响代码的结果,但是在多线程中可能会出问题)

解决方案:synchronized :不仅能保证原子性,还能保证内存可见性,还能保证指令重排序

3.synchronized的具体用法

synchronize 原意是同步,同步在计算机中有许多的意思.

在多线程的线程安全中,同步指的是"互斥"

在IO或者网络编程中,同步叫做"异步",此处的同步和互斥没有任何关系,和线程也没关系了.

使用方式:

1.直接修饰普通的方法

针对当前对象进行加锁

2.修饰一个代码块

3.修饰一个静态的方法

加锁的底层:

4.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值