线程的那点事二----线程同步和线程安全

线程安全:

实际开发中,使用多线程程序的情况很多,如银行排号系统、火车站售票系统等。这种多线程的程序通常会发生
问题,以火车站售票为例:

public class ThreadSafeTest implements Runnable{
	int num =10;
	public void run() {
		 while(true){
			 if(num>0){
				 try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				 System.out.println("tickets:"+num--);
			 }
		 }
		
	}
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

运行结果:

 

从上图可以看出,最后打印售剩下的票为负数,这样就出现了问题。这是由于同时创建了4个线程,这4个线程执行run()方法,在num变量为1时,线程1、线程2、线程3、线程4都对num变量有存储功能,当线程1执行run()方法时,还没有来得及递减操作,就制定它调用sleep()方法进入就绪状态,这时线程2、线程3、线程4都进入了run()方法,发现num变量依然大于0,但此时线程1休眠时间已到,将num变量值递减,同时线程2、线程3、线程4也都对num变量进行递减操作,从而产生了负值。

线程的同步机制:

那么该如何解决资源共享的问题呢?这时就需要给共享资源上一道锁,这就好比一个人上洗手间,他进入洗手间后会将门锁上,出来时再将门打开,然后其他人才可以进入。

在Java中提供了同步机制,可以有效地防止资源冲突。同步机制使用synchronizied关键字。

例如:

public class ThreadSafeTest implements Runnable{
	int num =10;
	public void run() {
		 while(true){
			 synchronized ("") {
				 if(num>0){
					 try {
						Thread.sleep(100);
					} catch (Exception e) {
						e.printStackTrace();
					}
					 System.out.println("tickets:"+num--);
				 }
			}
			 
		 }
		
	}
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

运行结果:

从上图可以看出,打印到最后票数没有出现负数,这是因为将资源放置在了同步块中。这个同步块也被称为临界区,它使用synchronized关键字,语法为:synchronized (Object) {}

通常将共享资源放在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查该对象的标志位,如果状态为0状态,表明此同步块中存在其他线程在运行。这时该线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码为止。这时该对象的标志位被设置为1,该线程才能执行同步块中的代码,并将Object对象的标志位设置为0,防止其他线程执行同步块中的代码。

同步方法:

同步方法就是在方法前面修饰synchronized关键字的方法,语法为:synchronized void f(){}

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。

public void run(){
		while(true){
			doit();
		}
	}
	public synchronized void doit(){
		while(true){
			 
				 if(num>0){
					 try {
						Thread.sleep(100);
					} catch (Exception e) {
						e.printStackTrace();
					}
					 System.out.println("tickets:"+num--);
				 } 
		 }
	}

猜你喜欢

转载自blog.csdn.net/weixin_42759988/article/details/89323016