1、概述
在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,如两个人同时说话,两个人同时过同一个独木桥等,所以在多线程变成中需要防止这些资源访问的冲突。Java提供了线程同步的机制来防止资源访问的冲突。
2、线程安全
实际开发中,使用多线程程序的情况很多,如银行排号系统、火车站售票系统等,这种多线程的程序通常会发生问题,以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客的功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程已执行完成判断是否有票的操作,并得出票数大于0的结论,于是它也执行售出操作,这样就会产生负数。所以在编写多线程程序时,应该考虑到线程安全问题。实质上线程安全问题来源于两个线程同时存取单一对象的数据。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.lzw; 2 3 public class ThreadSafeTest implements Runnable { 4 int num = 10; // 设置当前总票数 5 6 public void run() { 7 while (true) { 8 if (num > 0) { 9 try { 10 Thread.sleep(100); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 System.out.println("tickets" + num--); 15 } 16 } 17 } 18 19 public static void main(String[] args) { 20 ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象 21 Thread tA = new Thread(t); // 以该类对象分别实例化4个线程 22 Thread tB = new Thread(t); 23 Thread tC = new Thread(t); 24 Thread tD = new Thread(t); 25 tA.start(); // 分别启动线程 26 tB.start(); 27 tC.start(); 28 tD.start(); 29 } 30 }
3、线程同步机制
a、同步块
在Java中提供了同步机制,可以有效防止资源冲突。同步机制使用synchronized关键字。这个同步块也被称为临界区,它使用synchronized关键字建立,语法如下:
synchronized(Object){
}
通常将共享资源的操作防止在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别是0和1.一个线程运行到同步块时首先检查该对象的标志位,如果未0状态,表明此同步块在其他线程在运行。这时该线程处于就绪状态,知道处于同步块中的线程执行完同步块中的代码为止。这时该对象的标志位被设置为1,该线程才能执行同步块中的代码,并将Object对象的标志位设置为0,防止其他线程执行同步块中的代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.lzw; 2 3 public class ThreadSafeTest implements Runnable { 4 int num = 10; 5 6 public void run() { 7 while (true) { 8 synchronized ("") { 9 if (num > 0) { 10 try { 11 Thread.sleep(1000); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 System.out.println("tickets" + --num); 16 } 17 } 18 } 19 } 20 21 public static void main(String[] args) { 22 ThreadSafeTest t = new ThreadSafeTest(); 23 Thread tA = new Thread(t); 24 Thread tB = new Thread(t); 25 Thread tC = new Thread(t); 26 Thread tD = new Thread(t); 27 tA.start(); 28 tB.start(); 29 tC.start(); 30 tD.start(); 31 } 32 }
b、同步方法
同步方法就是在方法前面修饰synchronized关键字的方法,其语法如下:
synchronized void(){}
当某个对象调用了同步方法时,该对象上的其它同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public synchronized void doit() { //定义同步方法 2 if(num>0) { 3 try { 4 Thread.sleep(10); 5 }catch(Exception e){ 6 e.printStackTrace();; 7 } 8 System.out.println("tickets" + --num); 9 } 10 } 11 public void run() { 12 while(true) { 13 doit(); //在run()方法中调用该同步方法 14 } 15 }
注意:将共享资源的操作放置再同步方法中,运行结果与使用同步块的结果一致。