多线程之 线程安全 、锁 以及 线程同步

什么是线程安全

所谓线程安全是指我们要确保在多条线程访问的时候,程序能够按照我们预期的行为去执行。
  我们通过一个案例去模拟一下线程安全的问题
  假设开设多个窗口卖票,窗口我们用线程来模拟。

public class Demo_2 {
	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();
	}
}

class Ticket implements Runnable {
	//共20票
	int ticket = 20;

	@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--);
			}
		}
	}
}

执行结果 在这里插入图片描述
 我们会发现,上面的票出现了重复
其实线程安全出现是因为多线程在读写同一个临界资源时发生的。临界资源包括:
  多线程共享实例变量
  多线程共享静态的公共变量
若有多个线程执行写操作时,我们就需要考虑到线程同步问题,否则就可能会发生线程安全问题。

线程同步(线程安全处理Synchronized)

Java中提供了线程同步机制,能够解决线程安全问题。
  线程同步的方法有两种:
  同步代码块
  同步方法

同步代码块:

synchronized(锁对象){
    可能产生线程安全问题的代码
  }
同步代码块中的锁对象可以时任意的,但是对于多个线程必须要保证使用同一个锁对象才能保证线程安全。
我们将售票案例中的类改为如下:

class Ticket implements Runnable {
	//共20票
	int ticket = 20;
	
	//定义锁对象
	Object lock = new Object();

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

使用了同步代码块,上述的线程安全问题就得到了解决。

同步方法

在声明方法时,加上synchronized。

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

使用同步方法的方式,对Ticket方法进行修改

class Ticket implements Runnable {
	//共20票
	int ticket = 20;
	//Object lock = new Object();

	@Override
	public synchronized void run() {
		//模拟卖票
		while(true){
			//synchronized (lock){
			if (ticket > 0) {
				//模拟选坐的操作
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
			}
		}
	}
	}
//}
静态同步加锁:
public static synchronized void method(){
   可能会产生线程安全问题的代码
}

静态方法锁的对象是类对象,每个类都有唯一的类对象,获取类对象的方法:类名.class
静态方法和非静态方法同时声明了synchronized,他们之间是非互斥关系,
原因在于:静态方法锁的是类对象,非静态方法锁的是当前方法所属的对象。

死锁

使用同步锁有一个弊端:当线程中出现了多个同步时(多个锁),如果同步中套用了其他同步时。这时容易引发一种现象。程序出现了无限等待。这种现象我们称为死锁。我们要尽量避免这种情况的出现

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

我们对死锁进行如下代码演示:

public class Demo_4 {
	//测试类
	public static void main(String[] args) {
		//创建线程任务类对象
		ThreadTask task = new ThreadTask();
		//创建两个线程
		Thread t1 = new Thread(task);
		Thread t2 = new Thread(task);
		//启动线程
		t1.start();
		t2.start();
	}
}
 //锁对象
  class MyLock {
		public static final Object lockA = new Object();
		public static final Object lockB = new Object();
	}
//线程任务类
class ThreadTask implements Runnable{
		int x = new Random().nextInt(1);//0,1
		//指定线程要执行的任务代码
		@Override
		public void run() {
			while(true){
				if (x%2 ==0) {
					//情况一
					synchronized (MyLock.lockA) {
						System.out.println("LockA");
						synchronized (MyLock.lockB) {
							System.out.println("LockB");
							System.out.println("天上小鸟在飞!");
						}
					}
				} else {
					//情况二
					synchronized (MyLock.lockB) {
						System.out.println("LockB");
						synchronized (MyLock.lockA) {
							System.out.println("LockA");
							System.out.println("地上有只猪在跑");
						}
					}
				}
				x++;
			}
		}
}

在这里插入图片描述
本应该无限循环,却因为产生了死锁而不能执行。

Lock接口

经过查阅API,我们会发现,Lock实现提供了比使用synchronized方法和语法可获得更广泛的锁定操作。
Lock接口中常用的方法:  
  lock() 获取锁
  unlock 释放锁
Lock提供了一个更加面向对象的锁,在该锁中提供了更多的操作锁的功能。
我们使用Lock接口中的lock()和unlock()方法代替同步,将案例中的Ticket类进行修改。

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

猜你喜欢

转载自blog.csdn.net/he_zhen_/article/details/87736685