Java学习——线程同步(synchronized):同步代码块,同步方法,Lock锁 处理线程安全问题

1.同步代码块:

卖票案例出席线了线程安全问题
卖出了不存在和重复的票

解决1:使用同步代码块
格式:

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

注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个
3锁对象作用
把同步代码块锁住,只让一个线程在同步代码块中执行

【同步技术原理】

以卖票案例中的3个线程为例:
使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁或对象监视器

	3个线程一起抢夺CPU的执行权,谁抢到了谁执行run方法,进行卖票
	t0抢到了CPU的执行权,执行run方法,遇到synchronized代码块
	这时t0会检查synchronized代码块是否有锁对象
	发现有,【就会获取到锁对象,进入到同步中执行】
	
	t1抢到了CPU的执行权,执行run方法,遇到synchronized代码块
	这时t1会检查synchronized代码块是否有锁对象
	【发现没有,t1,就会进入到阻塞状态,会一直等待t0线程归还锁对象,一直到t0线程执行完同步中的代码,会把锁对象归还给同步代码块,t1才能继续获取到锁对象进入到同步中执行】
	
	 t2同理。
	 
由此我们可以得出:【同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去】

**【同步保证了只能有一个线程在同步中执行共享数据,保证了安全】**但由于程序频繁的判断锁,获取锁,释放锁,程序的效率会降低。

代码演示

public class RunnableImpl implements Runnable{

    //定义一个多线程共享的票源
    private int ticket = 100;

    //创建一个锁对象
   Object lock = new Object();

    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票偶重复执行
        while (true) {
            synchronized (lock) {
                //先判断票是否存在
                if(ticket > 0) {
                    //票存在,卖票 ticket--
                    //提高安全问题出现的概率,让程序睡眠
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //票存在,卖票 tichet--
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "正在卖第:" + ticket-- +"张票。");
                }
            }
        }
    }
}

Java学习——线程同步(synchronized):同步代码块,同步方法,Lock锁 处理线程安全问题_第1张图片

2.同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
使用步骤:
1.把访问共享数据的代码拿出来放到一个方法中
2.在方法上添加synchronized修饰符

格式:定义方法的格式
        修饰符 synchronized 发挥值类型 方法名(参数列表) {
            可能出现线程安全的代码(访问了共享数据的代码)
        }

同步方法也会把方法内部的代码锁住
只让一个线程执行
同步方法的锁对象是谁,就是实现类对象 new RunnableImpl()
也就是this

代码演示

public class RunnableImpl implements Runnable{

    //定义一个多线程共享的票源
    private int ticket = 100;

    //设置线程任务:卖票
    @Override
    public void run() {
        while (ticket > 0) {
           payTicket();
        }
    }
    /*
        定义一个同步方法
        同步方法也会把方法内部的代码锁住
        只让一个线程执行
        同步方法的锁对象是谁,就是实现类对象  new RunnableImpl()
        也就是this
    */
    public synchronized void payTicket() {
        //先判断票是否存在
        if(ticket > 0) {
            //票存在,卖票 ticket--
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ "-->正在,卖第"+ ticket + "张票");
            ticket--;
        }
    }
}

Java学习——线程同步(synchronized):同步代码块,同步方法,Lock锁 处理线程安全问题_第2张图片

Lock锁

java.util.concurrent.locks.Lock接口
Lock 实现提供了比synchronized方法和语句可获得的更广泛的锁定操作
Lock接口中的方法:
 public void lock() //:加同步锁。
 public void unlock() //:释放同步锁。

【使用步骤:】
1.在成员位置创建一个ReentrantLock对象
2.在可能会出现安全问题的代码前调用Lock接口中方法lock获取锁
3.在可能会出现安全问题的代码后调用Lock接口中方法unlock释放锁

程序演示

public class RunnableImpl implements Runnable{

    //定义一个多线程共享的票源
    private int ticket = 100;
   // 1.在成员位置创建一个ReentrantLock对象
    Lock lock = new ReentrantLock();
    //设置线程任务:卖票
    @Override
    public void run() {
        while (true) {
            //2.在可能会出现安全问题的代码前调用Lock接口中方法lock获取锁
            lock.lock();
            //先判断票是否存在
            if(ticket > 0) {
                //票存在,卖票 ticket--
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ "-->正在,卖第"+ ticket + "张票");
                ticket--;
            }

            //3.在可能会出现安全问题的代码后调用Lock接口中方法unlock释放锁
            lock.unlock();
        }
    }
}

Java学习——线程同步(synchronized):同步代码块,同步方法,Lock锁 处理线程安全问题_第3张图片

你可能感兴趣的:(线程安全)