java基础学习之线程同步机制

本文讲解了多线程下共享数据导致的线程安全问题,通过实例演示了如何使用同步代码块、synchronized方法以及Lock接口来解决。重点介绍了同步锁的概念、ReentrantLock的使用以及其对提高代码效率的影响。
摘要由CSDN通过智能技术生成

java基础学习之线程同步机制

一、多线程访问共享数据可能会产生线程安全问题

多线程访问共享数据可能会产生线程安全问题
以卖票为例:

public class RunnableImpl1 implements Runnable {
    private int count = 100;//初始100张票
    @Override
    public void run() {
        while(true){
            if(count>0){
                System.out.println(Thread.currentThread().getName()+"在卖第"+count+"张票");
                count--;
            }
        }
    }
}

public class Test {
    public static void main(String[] args) {
        RunnableImpl1 runnableImpl1 = new RunnableImpl1();
        Thread thread = new Thread(runnableImpl1);
        Thread thread1 = new Thread(runnableImpl1);
        Thread thread2 = new Thread(runnableImpl1);
        thread.start();
        thread1.start();
        thread2.start();
    }
}

程序运行过程中会出现卖同一张票的情况。

二、解决线程安全问题的一种方案:使用同步代码块

格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

注意:

  1. 同步代码块中的锁对象,可以使用任意的对象。
  2. 但是必须保证多个线程使用的锁对象是同一个。(特别注意)
  3. 锁对象作用:
    把同步代码块锁住,只让一个线程运行同步代码块。
public class SocketTest implements Runnable{
    private int count = 100;
    String a = "aaa";//a要定义在run()方法的外面,如果定义在里面线程调用start()方法,执行run()方法的时候,会创建新的对象a,就不能保证是同一个对象
    @Override
    public void run() {
        synchronized (a){//使用同步代码块
            while(true){
                if(count>0){
                    System.out.println(Thread.currentThread().getName()+"在卖第"+count+"张票");
                    count--;
                }
            }
        }
    }
}

public class Test {
    public static void main(String[] args) {
        SocketTest runnableImpl1 = new SocketTest();
        Thread thread = new Thread(runnableImpl1);
        Thread thread1 = new Thread(runnableImpl1);
        Thread thread2 = new Thread(runnableImpl1);
        thread.start();
        thread1.start();
        thread2.start();
    }
}

同步技术的原理

使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器
3个线程一起抢夺cpu的执行权,谁抢到了谁执行run方法进行卖票

t0线程抢到了cpu的执行权,执行run方法,遇到synchronize代码块
这时t0线程会检查synchronize代码块是否有锁对象
发现有,就会获取到锁对象,进入到同步中执行

t1线程抢到了cpu的执行权,执行run方法,遇到synchronize代码块。
这时t1会检查synchronize代码块是否有锁对象,发现没有,t1会进入到阻塞状态,会一直等待t0线程归还锁对象。
一直到t0线程执行完同步中的代码,会把锁对象归还给同步代码块,t1才能获取到锁对象,进入到同步代码块中执行。

注意:
同步保证了只能有一个线程在同步中执行共享数据
程序频繁地判断锁,获取锁,释放锁,程序的效率会降低。

特别注意:
把可能会操作到共享资源A的不同线程中的代码用锁对象1锁住。
把可能会操作到共享资源B的不同线程中的代码用锁对象2锁住。(意会一下)

同步方法

使用synchronized修饰的方法,就叫做同步方法,保证线程A执行该方法的时候,其他线程只能在方法外等着。
把可能产生线程安全问题的代码放到同步方法里。

格式:
public synchronized 返回值类型 方法名(){
}

例子:

public class SocketTest implements Runnable{
    private int count = 100;
    String a = "aaa";
    @Override
    public void run() {
        synchronized (a){//使用同步代码块
            while(true){
                method();
            }
        }
    }

    public synchronized void method(){
        if(count>0){
            System.out.println(Thread.currentThread().getName()+"在卖第"+count+"张票");
            count--;
        }
    }
}

同步锁(锁对象)是谁?

对于非static方法,同步锁就是this,调用方法的当前对象
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

Lock锁

锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。

解决线程安全问题的第三种方法:使用Lock锁
java.util.concurrent.locks.Lock接口
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

Lock接口中的方法:
void lock()获取锁
void unlock()释放锁

java.util.concurrent.locks.ReentrantLock impelments Lock
ReentrantLock是Lock接口的实现类。

使用Lock锁时一般应使用这样的结构:
Lock l = …; (new ReentrantLock();或者其它的Lock接口的实现类对象)
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SocketTest implements Runnable {
    private static int count = 100;
    String a = "aaa";
    Lock l = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            l.lock();
            try{
                if (count > 0) {
                    System.out.println(Thread.currentThread().getName() + "在卖第" + count + "张票");
                    count--;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                l.unlock();//无论异常是否发生,都会释放锁
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值