线程安全java
缘由:当多个线程访问一个共享的资源时,会产生线程安全的问题。c++
解决方法:用synchronized关键字作为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。典型的用法以下:安全
synchronized(锁){ //这个锁是共享对象多线程
临界区代码 并发
}ide
/** * 两个线程共同打印a~z,考虑线程安全 * */ public class PrintLetters implements Runnable { private char c = 'a'; public synchronized boolean print() { if (c <= 'z') { System.out.println(Thread.currentThread().getName() + " --- " + c); try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } c++; return true; } return false; } @Override public void run() { boolean flag = print(); while (flag) { flag = print(); } } public static void main(String[] args) { PrintLetters printLetters = new PrintLetters(); Thread thread1 = new Thread(printLetters); Thread thread2 = new Thread(printLetters); thread1.setName("线程-1"); thread2.setName("线程-2"); thread1.start(); thread2.start(); } }
上面代码属于public synchronized void add(int num)状况,其锁就是这个方法所在的对象。同理,若是方法是public static synchronized void add(int num),那么锁就是这个方法所在的class。spa
线程间的相互做用:线程之间须要一些协调通讯,来共同完成一件任务。能够调用java.lang.Object中的wait()、notify()和notifyAll()方法,这些方法是final的,不能被重写。.net
wait():使当前线程进入放弃对象锁进入wait pool中等待。直到其余线程调用notify()或notifyAll()方法唤醒该线程。要确保线程调用wait()方法时拥有对象锁,即wait()方法必须在synchronized方法或者synchronized方法块中调用。
线程
notify()、notifyAll():code
notify():唤醒一个处于等待当前对象锁的线程。若是多个线程处于等待状态,会随机选择一个线程唤醒。同时,被唤醒的线程暂时不能被执行,须要等到当前线程放弃对象锁。见:http://my.oschina.net/u/1757476/blog/420169 线程的生命周期。
notifyAll():唤醒全部等待当前对象锁的线程,变成等待该对象上的锁。一旦该对象被解锁,它们就会去竞争。
/** * 模拟卖票:刘、关、张三人买票,售票员只有一张5元零钱,票价5元;张飞拿20元排在刘、关前面,刘、关二人各拿了5元。 * 线程间的通讯 */ public class TicketHouse implements Runnable { private int fiveCount = 1, tenCount = 0, twentyCount = 0; public synchronized void buy() { String name = Thread.currentThread().getName(); //张飞 20元 if ("张飞".equals(name)) { if (fiveCount < 3) { try { System.out.println("5元面值:" + fiveCount + ". 张飞等待..."); wait(); System.out.println("卖一张票给" + name + ",找零15. 5元面值:" + fiveCount); } catch (InterruptedException e) { e.printStackTrace(); } } } else if ("关羽".equals(name) || "刘备".equals(name)) { fiveCount++; System.out.println("卖一张票给" + name + ",钱正好. 5元面值:" + fiveCount); } if (fiveCount == 3) { notifyAll(); } } @Override public void run() { buy(); } public static void main(String[] args) { Runnable runnable = new TicketHouse(); Thread th1 = new Thread(runnable); Thread th2 = new Thread(runnable); Thread th3 = new Thread(runnable); th1.setName("刘备"); th2.setName("关羽"); th3.setName("张飞"); th3.start(); th1.start(); th2.start(); } }
/** * 两个线程交替打印a~z * */ public class PrintLetters2 implements Runnable { private char c = 'a'; @Override public void run() { while (c <= 'z') { print(); } } public synchronized void print() { if (c <= 'z') { System.out.println(Thread.currentThread().getName() + "---" + c); c++; notify(); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Runnable runnable = new PrintLetters2(); Thread th1 = new Thread(runnable); Thread th2 = new Thread(runnable); th1.setName("线程-1"); th2.setName("线程-2"); th1.start(); th2.start(); } }