【了解Java线程安全】线程同步

活动地址:CSDN21天学习挑战赛

目录

​一、线程安全

1.1 线程安全问题

 二、线程同步

三、同步代码块

四、同步方法

五、锁机制

​一、线程安全

1.1 线程安全问题

        ①在多线程编程时,由于CPU对线程的调度具有一定的随机性,当多个线程访问同一个资源时,如果控制不好,也会造成数据的不正确性;

        ②线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

举例:通过模拟火车票的抢票过程,具象化演示线程安全问题

package Synchronized;

//线程不安全案例——不安全买票(有负数)
public class UnsafeBuyTicket{
	public static void main(String[] args) {
		BuyTicket station = new BuyTicket();
		
		new Thread(station,"购票人1").start();
		new Thread(station,"购票人2").start();
		new Thread(station,"抢票软件").start();
		
	}
	
}

class BuyTicket implements Runnable{
	
	//票
	private int ticketNums = 10;
	boolean flag = true;//外部停止方式
	
	@Override
	public void run() {   //重写run函数,执行买票操作
		while (flag) {
			try {
				buy();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	
	private void buy() throws InterruptedException {
		//判断是否有票
		if(ticketNums <= 0) {
			flag = false;
			return;
		}
		//模拟延时
		Thread.sleep(100);
		
		//买票
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}
	
}

        由于线程的调度室随机的,每次运行结果差异较大。第一次运行结果如下:

        第二次运行结果:

         发现运行结果出现了两个问题:
                1)相同的票数,比如7这张票被买了三回。
                2)不存在的票,比如0票与-1票,是不存在的。
                这种问题,几个购买者的购买操作(线程)同时进行了,且争夺同一个资源,这种问题称为线程不安全

 二、线程同步

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

        同步机制等价于一个“等待机制”:

                当多个线程要访问一个资源对象,他们就会进入这个对象的等待池形成队列,前面的线程访问该对象时获得“锁”独占资源,资源使用结束后才释放“锁”,下一个线程再去访问该资源对象(整个“队列+锁”的同步机制  类似于 洗手间的排队等待上厕所)。

        同步机制的使用有三种方式:

                1)同步代码块

                2)同步方法

                3)锁机制

三、同步代码块

        格式:

        synchronized( Obj ){
             需要同步操作的代码;
        }

        Obj为同步锁:1.)锁对象 可以是任意类型

                                2)多个线程对象要使用同一把锁(在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等待(BLOCKED阻塞状态))

        使用同步代码块解决代码:

package Synchronized;

//线程不安全案例——不安全买票
public class UnsafeBuyTicket{
	public static void main(String[] args) {
		BuyTicket station = new BuyTicket();
		
		new Thread(station,"购票人1").start();
		new Thread(station,"购票人2").start();
		new Thread(station,"抢票软件").start();
		
	}
	
}

class BuyTicket implements Runnable{
	
	//票
	private int ticketNums = 10;
	boolean flag = true;//外部停止方式
	
	Object lock = new Object();  //定义一个同步锁(Object对象)
	
	@Override
	public void run() {
		synchronized(lock) {   //run函数运行过程中使用synchronized同步锁
			
			while (flag) {
				try {
					buy();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
	
	
	private void buy() throws InterruptedException {
		//判断是否有票
		if(ticketNums <= 0) {
			flag = false;
			return;
		}
		//模拟延时
		Thread.sleep(100);
		
		//买票
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}
}

        运行结果:

        可以看到,不存在买到相同的票 ,或是票号不存在的情况,此时线程安全。

四、同步方法

        同步方法 :使用synchronized关键字修饰的方法。保证A线程执行该方法的时候,其他线程只能在方法外等待。

        格式:

public synchronized void method(){
   可能会产生线程安全问题的代码 ;//方法的定义
}

         使用同步方法解决代码: 

package Synchronized;

//线程不安全案例——不安全买票
public class UnsafeBuyTicket{
	public static void main(String[] args) {
		BuyTicket station = new BuyTicket();
		
		new Thread(station,"购票人1").start();
		new Thread(station,"购票人2").start();
		new Thread(station,"抢票软件").start();
		
	}
	
}

class BuyTicket implements Runnable{
	
	//票
	private int ticketNums = 10;
	boolean flag = true;//外部停止方式
	
	@Override
	public void run() {
		while (flag) {
			try {
				buy();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	
	//synchronized同步方法,锁的是this
	private synchronized void buy() throws InterruptedException {
		//判断是否有票
		if(ticketNums <= 0) {
			flag = false;
			return;
		}
		//模拟延时
		Thread.sleep(100);
		
		//买票
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}
	
}

        运行结果:

         同样地,不存在买到相同的票 ,或是票号不存在的情况,此时线程安全,并且线程的调度更加随机。

五、锁机制

        Lock锁(又称同步锁),通过JUC模块的java.util.concurrent.locks.Lock 接口,显式定义同步锁对象,将加锁释放锁方法化,即调用“lock.lock(); //加锁”和“lock.unlock();//解锁”的操作。

格式如下:

public class T{
...... ;  //定义变量
   
        Lock lock = new ReentrantLock();    //创建一个锁对象
        /*    
         * 重写run()函数,执行方法动作    
         */    
        @Override    
        public void run() {    
//               try{         

                 

                lock.lock();            //加锁
                
                try {                
                        Thread.sleep(...); 

                        ......  ;                       //保证线程安全的代码     
                } catch (InterruptedException e) {                                
                        e.printStackTrace();                    
                }         

//                       }

//                finally{           //如果同步代码有异常,要将unlock()写入fianally块,保证锁的释放

        
                lock.unlock();        //释放锁


//                        }       
        }        
}    

        代码如下:

package Synchronized;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//线程不安全案例——不安全买票(有负数)
public class UnsafeBuyTicket{
	public static void main(String[] args) {
		BuyTicket station = new BuyTicket();
		
		new Thread(station,"购票人1").start();
		new Thread(station,"购票人2").start();
		new Thread(station,"抢票软件").start();
		
	}
	
}

class BuyTicket implements Runnable{
	
	//票
	private int ticketNums = 10;
	boolean flag = true;//外部停止方式
	
	
	//同步锁Lock解决线程安全问题
	Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		while (flag) {
			lock.lock();    //由lock方法显式加锁
			try {
				buy();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			lock.unlock();     //由unlock方法显式释放锁
		}
		
	}
	

	private void buy() throws InterruptedException {
		//判断是否有票
		if(ticketNums <= 0) {
			flag = false;
			return;
		}
		//模拟延时
		Thread.sleep(100);
		
		//买票
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}
}

        运行结果如下,此时线程安全。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锅小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值