什么是线程安全:
当多个线程同时共享同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
线程安全的解决方式:
使用多线程之间同步或使用锁(lock)可以解决线程安全问题。其核心在于将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,然后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
什么是多线程之间的同步:
多个线程共享同一个资源的环境下,每个线程工作时不会受到其他线程的干扰称之为多线程之间的同步。
解决线程安全:
使用同步代码块:
synchronized(同一个对象) { 可能会发生线程冲突问题 }
注意:
在同步代码块中,多个线程必须使用的是同一把锁,即同一个对象。
一般情况下,在使用Runnable实现的线程类中,我们会使用this作为锁对象。
在使用Thread继承的线程类中,一般会使用其Class对象(Class对象在JVM中只会创建一次)。
class Ticket{ private int number = 30; public synchronized void sale() { if (number > 0) { System.out.println(Thread.currentThread().getName() + "卖出第:\t" + (number--) + "\t 还剩下:" + number); } } } public class SaleTicket { public static void main(String[] args){ //创建资源对象 Ticket tc = new Ticket(); //创建AA线程、 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { //卖票 tc.sale(); } } }, "AA").start(); //创建BB线程、 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { //卖票 tc.sale(); } } }, "BB").start(); //创建CC线程、 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 40; i++) { //卖票 tc.sale(); } } }, "CC").start(); } }
注意:如果使用Thread继承的方式实现多线程,那么同步方法需要是一个静态的方法!
使用Lock解决线程安全:
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。Lock实现提供更广泛的锁定操作可以比使用 synchronized获得方法和声明更好。他们允许更灵活的结构,可以有完全不同的特性,可以支持多个相关的 Condition对象。Lock提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。