Java_多线程实现同步

waitui00 2020-01-13

多线程之间实现同步

理解线程安全

synchronized用法

死锁

Java内存模型

Vlolatile 关键字

ThreadLock 关键字

理解线程安全

什么是线程安全?

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

但是做读操作是不会发生数据冲突问题

线程安全解决办法:

使用多线程之间同步synchronized或使用锁(lock)。

原理:

同一时间内,只让当前一个线程进行对数据进行执行操作。当当前线程执行完成后释放锁,才能让其他线程进行操作执行。避免了同时对数据的操作,这样的话就可以解决线程的不安全问题,也就实现了数据的同步。

synchronized用法:

synchronized有两种用法:

第一种:将可能会发生线程安全问题的代码,给包括起来,称为同步代码块。

代码如下:

synchronized(锁){

  //可能会发生线程冲突问题的代码块

}

第二种:在方法上修饰synchronized,称为同步函数。

代码如下:

public synchronized void  方法名() {

  //可能会发生线程冲突问题的代码块

}

若在方法上加上static静态关键字则成为静态同步函数;

静态同步函数使用的锁是该函数所属字节码文件对象;

可以使用getClass()的方法获取,也可以用当前类名.class 表示。

同步方法和同步代码块的区别:

同步函数默认使用this或者当前类class对象作为锁;

同步代码块则可以选择用什么来加锁,并且可以只同步会发生线程安全问题的部分代码而非整个方法;

对象如同锁,持有锁的线程可以在同步中执行 ;

没持有锁的线程即使获取CPU的执行权,也进不去 ;

同步的前提: 

1,要有两个或者两个以上的线程 ,否则没有意义;

2,多个线程使用同一个锁 ;

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

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

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

死锁

什么是多线程死锁?

在同步中嵌套同步,程序运行时多个线程之间互相拿着对方需要的锁,导致锁无法释放也无法继续执行。

多线程的三大特性

原子性:

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

可见性:

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

有序性

程序执行的顺序按照代码的先后顺序执行。

 

Java中的内存模型

Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。

Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。

线程之间的通信

线程的通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种共享内存和消息传递。

在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信。

共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

但是,共享变量存放在主内存中,而每个线程都有自己的本地内存,所以,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,因此就会发生线程安全问题。

在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()和notify()。

Volatile

什么Volatile?

volatile是java提供的一种同步手段,它是轻量级的同步,只能修饰变量,只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。

任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都会及时写在主存。因此对于volatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。

volatile与synchronized区别:

仅靠volatile不能保证线程的安全性。(原子性)

①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法。

②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。

synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

线程安全性

线程安全性包括两个方面,①可见性。②原子性。

从上面可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。

ThreadLock

什么是ThreadLock?

ThreadLock提供了线程内存储变量的能力,是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal的接口方法:

void set(Object value):设置当前线程的线程局部变量的值。

public Object get():该方法返回当前线程所对应的线程局部变量。

public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。

需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。

这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

ThreadLocal特性:

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是:

Synchronized是通过线程等待,牺牲时间来解决访问冲突;

ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突;

相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

因此,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。

相关推荐