Java之多线程安全性问题

时间:2021-10-20 13:10:20

小结:

同步方式:

a, 同步代码块锁对象可以是任意的对象

synchronized (锁对象) {
	可能会产生线程安全问题的代码
}

b, 同步方法  锁对象是this

public synchronized void method(){
   	可能会产生线程安全问题的代码
}

    bb 静态同步方法 锁对象是本类名.class

public static synchronized void method(){
可能会产生线程安全问题的代码
}

c. Lock接口   

	//创建Lock锁对象
	Lock ck = new ReentrantLock();
         //上锁
		ck.lock();
	 添加可能会产生线程安全问题的代码		
	 //解锁
                ck.unlock();
	}

sleep: 不释放锁对象, 在休眠的时间内,不能唤醒

 wait(): 释放锁对象, 在等待的时间内,能唤醒

多线程安全问题

案例:

电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。

我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)

public class ThreadDemo {
	public static void main(String[] args) {
		//创建票对象
		Ticket ticket = new Ticket();
		
		//创建3个窗口
		Thread t1  = new Thread(ticket, "窗口1");
		Thread t2  = new Thread(ticket, "窗口2");
		Thread t3  = new Thread(ticket, "窗口3");
		
		t1.start();
		t2.start();
		t3.start();
	}
}
Java之多线程安全性问题
public class Ticket implements Runnable {
	//共100票
	int ticket = 100;

	@Override
	public void run() {
		//模拟卖票
		while(true){
			if (ticket > 0) {
				//模拟选坐的操作
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
			}
		}
	}

Java之多线程安全性问题

问题:

t0,t1,t2线程都可能出现:当各自的线程在执行完if(ticket >0)的判断后,cpu资源被抢占,线程处于阻塞状态进入等待。但是当三个线程各自拿到cpu资源时,已经不需要做if(ticket >0) 的判断,这样就可能导致t0在执行完出票操作后,ticket = 0 ,而当t1,t2线程去执行的时候,ticket会出现负数的情况。

线程同步

同步代码块:

synchronized (锁对象) {
	可能会产生线程安全问题的代码
}

同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

public class Ticket implements Runnable {
	//共100票
	int ticket = 100;
	//定义锁对象
	Object lock = new Object();
	@Override
	public void run() {
		//模拟卖票
		while(true){
			//同步代码块
			synchronized (lock){
				if (ticket > 0) {
					//模拟电影选坐的操作
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
				}
			}
		}
	}
}
Java之多线程安全性问题
同步方法:( 推荐使用
public synchronized void method(){
   	可能会产生线程安全问题的代码
}
public class Ticket implements Runnable {
	//共100票
	int ticket = 100;
	//定义锁对象
	Object lock = new Object();
	@Override
	public void run() {
		//模拟卖票
		while(true){
			//同步方法
			method();
		}
	}

//同步方法,锁对象this
	public synchronized void method(){
		if (ticket > 0) {
			//模拟选坐的操作
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
		}
	}
}

同步方法之静态同步:

public static synchronized void method(){
可能会产生线程安全问题的代码
}

Q1同步方法有锁吗?

有的,同步方法中的锁对象为本类对象引用this

Q2:静态同步方法有锁吗?

有的,静态同步方法中的锁对象为本类类名.class

Q3:采用同步还有弊端吗

有的,如上例中,线程睡眠一旦产生异常,去捕获异常,同步方法不能结束,同步锁就不能被释放。因此JDK1.5后,采用Lock接口可替代synchronized

Lock接口:(强烈推荐使用

使用Lock接口,以及其中的lock()方法和unlock()方法替代同步

public class Ticket implements Runnable {
	//共100票
	int ticket = 100;
	
	//创建Lock锁对象
	Lock ck = new ReentrantLock();
	
	@Override
	public void run() {
		//模拟卖票
		while(true){
			//synchronized (lock){
			ck.lock();
				if (ticket > 0) {
					//模拟选坐的操作
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
				}
			ck.unlock();
			//}
		}
	}
}

死锁

当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。

synchronzied(A锁){
	synchronized(B锁){
         
        }
}

Java之多线程安全性问题

  

Q1sleep()和wait()方法的区别

         sleep: 不释放锁对象, 释放CPU使用权

                    在休眠的时间内,不能唤醒

         wait(): 释放锁对象, 释放CPU使用权

                    在等待的时间内,能唤醒

Q2:为什么wait(),notify(),notifyAll()等方法都定义在Object类中

         锁对象可以是任意类型的对象