java线程同步

线程同步:

当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。

要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制(synchronized)来解决。

为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。

那么怎么去使用呢?有三种方式完成同步操作:

  1、同步代码块。

  2、同步方法。

  3、锁机制。

同步代码块:

  同步代码块:synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实时互斥访问。

  格式:

    synchronized (同步锁) {

      需要同步操作的代码块;

    }

  同步锁:

    对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

      1、锁对象  可以是任意类型;

      2、多个线程对象要使用同一把锁;

      注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着 (BLOCKED)。

同步方法:

  同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

  格式:

    public synchronized void method(){

      可能会产生线程安全问题的代码

    }

  同步锁是谁?

    对于非static方法,同步锁就是this。

    对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

Lock锁:

   java.util.concurrent.locks.Lock 机制提供了比 synchronized 代码块和 synchronized 方法更广泛的锁定操作,

   同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

   Lock锁也称同步锁,加锁与释放锁方法化了,如下:

    public void lock() : 加同步锁

    public void unlock() : 释放同步锁

案列:

  电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个 (本场电影只能卖100张票)。

  我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票) 需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟

模拟票:

  

 1 class Ticket implements Runnable{
 2     private int ticket = 100;
 3     /*
 4     * 执行买票操作
 5     * */
 6     @Override
 7     public void run() {
 8         //每个窗口卖票的操作
 9         //窗口 永远开启
10         while (true){
11                 if (ticket > 0) {  //有票,可以卖
12                     //使用sleep模拟下出票时间
13                     try {
14                         Thread.sleep(100);
15                     } catch (InterruptedException e) {
16                         e.printStackTrace();
17                     }
18                     //获取当前线程对象的名字
19                     String name = Thread.currentThread().getName();
20                     System.out.println(name + "正在卖:" + ticket--);
21                 }
22         }
23     }
24 }

测试类:

  

 1 public class TestDemo1 {
 2     public static void main(String[] args) {
 3         //创建线程任务对象
 4         Ticket ticket = new Ticket();
 5         //创建三个窗口对象
 6         Thread t1 = new Thread(ticket,"窗口1");
 7         Thread t2 = new Thread(ticket,"窗口2");
 8         Thread t3 = new Thread(ticket,"窗口3");
 9 
10         //同时卖票
11         t1.start();
12         t2.start();
13         t3.start();
14     }
15 }

运行结果:

  

 

 发现程序出现了两个问题:

  1. 相同的票数,比如5这张票被卖了两回。

  2. 不存在的票,比如0票与-1票,是不存在的。

  这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全。

 

解决方法:

  方式一:同步代码块

    

 1 class Ticket implements Runnable{
 2     private int ticket = 100;
 3 
 4     Object lock = new Object();
 5 
 6     /*
 7     * 执行买票操作
 8     * */
 9     @Override
10     public void run() {
11         //每个窗口卖票的操作
12         //窗口 永远开启
13         while (true){
14             synchronized (lock) {
15                 if (ticket > 0) {  //有票,可以卖
16                     //使用sleep模拟下出票时间
17                     try {
18                         Thread.sleep(100);
19                     } catch (InterruptedException e) {
20                         e.printStackTrace();
21                     }
22                     //获取当前线程对象的名字
23                     String name = Thread.currentThread().getName();
24                     System.out.println(name + "正在卖:" + ticket--);
25                 }
26             }
27         }
28     }
29 }

 

  方式二:同步方法

    

 1 class Ticket implements Runnable{
 2     private int ticket = 100;
 3     /*
 4     * 执行买票操作
 5     * */
 6     @Override
 7     public void run() {
 8         //每个窗口卖票的操作
 9         //窗口 永远开启
10         while (true){
11             sellTicket();
12         }
13     }
14     public synchronized void sellTicket(){
15         if (ticket > 0){  //有票可以卖
16             //出票操作
17             //使用sleep模拟一下出票时间
18             try{
19                 Thread.sleep(100);
20             }catch (InterruptedException e){
21                 e.printStackTrace();
22             }
23             //获取当前线程对象的名字
24             String name = Thread.currentThread().getName();
25             System.out.println(name+"正在卖:"+ticket--);
26         }
27     }
28 
29 }

 

  方式三:Lock锁

    

 1 class Ticket implements Runnable{
 2     private int ticket = 100;
 3     Lock lock = new ReentrantLock();
 4     /*
 5     * 执行买票操作
 6     * */
 7     @Override
 8     public void run() {
 9         //每个窗口卖票的操作
10         //窗口 永远开启
11         while (true){
12             lock.lock();
13             if (ticket > 0){  //有票可以卖
14                 //出票操作
15                 //使用sleep模拟一下出票时间
16                 try{
17                     Thread.sleep(100);
18                 }catch (InterruptedException e){
19                     e.printStackTrace();
20                 }
21                 //获取当前线程对象的名字
22                 String name = Thread.currentThread().getName();
23                 System.out.println(name+"正在卖:"+ticket--);
24             }
25             lock.unlock();
26         }
27     }
28 }

 

posted @ 2021-03-09 13:24  安逸的坐姿  阅读(80)  评论(0编辑  收藏  举报