【java基础之多线程】线程同步

时间:2023-02-26 12:45:31
在开始线程同步之前,先来看两个名词

       并发访问:当使用多线程来访问同一个数据时,很容易出现线程安全的问题(并发访问)

       线程安全:并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(临界区),所以同一时刻最多只有一个线程处于临界区,从而保证线程的安全性

线程同步主要由四种方法:1.互斥区,就是锁了;2.条件变量;3.信号量;4.事件。基本上我们写程序用的就是锁了。为了保证线程同步,一般用如下两种方式:

1.  synchronized同步监视器

       1)步代码块

            使用同步监视器的通用方法,就是同步代码块。

            语法格式:synchronized(obj){ …… } //obj为同步监视器

            说明:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。

            任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完后,该线程会释放对同步监视器的锁定。Java程序允许使用任何对象作为同步监视器

            同步监视器的目的:阻止两个线程对同一个共享资源进行并发访问(推荐使用可能被并发访问的共享资源充当同步监视器)

      2)同步方法

            同步方法:使用synchronized关键字来修饰的方法。对于synchronized修饰的实例方法(非static方法),无需显式指定同步监视器,同步方法的同步监视器是this

            可变类与不可变类:不可变类总是线程安全的,因为它的对象状态不可变;可变对象需要额外的方法来保证其线程安全。

可变类的线程安全是以降低程序的效率为代价的,为了减少线程安全所带来的负面影响,程序可采用如下方法:

      1)不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步

      2)如果可变类有两种运行环境:单线程环境、多线程环境,则应为该类提供两种版本(线程安全与不安全版本)

            单线程环境下用线程不安全版本以保证性能,多线程环境下用线程安全版本以保证安全

            (synchronized可以修饰方法、代码块,但是不能修饰构造器、成员变量)

      3)释放同步监视器的锁定

            程序无法显式释放对同步监视器的锁定,线程会在如下几种情况下释放对同步监视器的锁定

            执行结束

            遇到break、return

            出现未处理的Error、Exception

            程序中执行了同步监视器对象的wait()方法

            无法释放同步监视器的情况

            程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器

            线程执行同步代码块时,其它线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器(避免使用suspend()、resume())

2. 同步锁

      同步锁:通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当(更强大的线程同步机制)