解决线程不安全问题,java中的线程同步机制

java中提供了三种解决线程安全的方法

为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。
有三种方式完成同步操作:
  1. 同步代码块。
  2. 同步方法。
  3. 锁机制。
 
1.同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
  
synchronized(同步锁){
  需要同步操作的代码,即可能会出现线程安全的代码
}
同步锁(也叫锁对象):
  对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
  1. 锁对象 可以是任意类型,如Object。
  2. 多个线程对象 要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
public class SaleTickets implements Runnable {

    private int tickets = 100;

    //新建一个锁对象
    Object obj = new Object();

    @Override
    public void run() {
//        让其不断的卖票
        while(true) {
//            使用同步代码块
            synchronized (obj){
                //            tickets必须大于0
                if (tickets > 0) {
//                取到票后,就休息10毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
//                打印出卖票信息
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");
//                票数实现-1
                    tickets--;
                }
            }
        }
    }
}

 

2. 同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method(){
  可能会产生线程安全问题的代码
}
注意:synchronized位于修饰符和返回值之间
 
此时有一个问题,对于同步方法,我们怎么没看到锁对象(同步锁)?
  对于非static方法,同步锁就是this。
    this即为线程的实现类对象
  对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
    因为this是创建对象后,才存在的;而static方法是优先于对象存在的;故对于static方法,锁对象就是所在类的字节码文件
 
public class SaleTickets02 implements Runnable {

    private int tickets = 100;

    @Override
    public void run() {
//        让其不断的卖票
        while(true) {
            lockTickets();
        }
    }

//    把有可能产生线程安全的代码放到方法里面
    public synchronized void lockTickets(){
        //            tickets必须大于0
        if (tickets > 0) {
//                取到票后,就休息10毫秒
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//                打印出卖票信息
            System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");
//                票数实现-1
            tickets--;
        }
    }
}

通过代码证明,非静态方法,锁对象就是this

//    把有可能产生线程安全的代码放到方法里面
    public /*synchronized*/ void lockTickets(){

        synchronized(this){
            //            tickets必须大于0
            if (tickets > 0) {
//                取到票后,就休息10毫秒
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                打印出卖票信息
                System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");
//                票数实现-1
                tickets--;
            }
        }
}

而对于静态方法,锁对象就是该方法所在类的字节码文件

//    把有可能产生线程安全的代码放到方法里面
    public static /*synchronized*/ void lockTickets(){
//    此时的锁对象就是方法所在类的字节码文件
        synchronized(SaleTickets02.class){
            //            tickets必须大于0
            if (tickets > 0) {
//                取到票后,就休息10毫秒
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                打印出卖票信息
                System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");
//                票数实现-1
                tickets--;
            }
        }
    }

证明this就是实现类对象

在实现类的run()方法中,我们插入如下语句:输出调用run方法的对象

System.out.println("this:     "+this);

 

然后测试类中,我们直接输出实现类对象

System.out.println("实现类对象:"+st);

输出结果如下:

 

 发现this就是实现类对象(由于新建三个线程故会输出三次this)

 

3.Lock锁 :java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

 

Lock是一个接口,故要使用Lock时,必须new一个其实现类,如ReentrantLock

Lock锁也称同步锁,加锁与释放锁方法化了,如下:
  public void lock() :加同步锁。
  public void unlock() :释放同步锁。
使用步骤:
  1.新建一个Lock对象,Lock  lk = new ReentrantLock();
  2.在可能出现线程安全的代码前调用lock()方法;
  3.在可能出现线程安全的代码后调用unlock()方法。
推荐使用try...finally,因为会无条件执行finally里面的释放锁的功能
  
Lock l = ...; 
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }

 

 
 
posted @ 2021-07-25 19:07  功不唐捐-纯小喜  阅读(153)  评论(0)    收藏  举报