出现线程不安全的三个条件
- 是多线程环境
- 线程之间有共享数据
- 对共享数据的操作是多句
处理线程安全方法:同步块/同步方法
多线程之生产者、消费者模式
生产者:
public class Producer implements Runnable {
private Milk milk;
public Producer(Milk milk){
this.milk = milk;
}
public void run() {
for (int i=1;i<10;i++){
try {
milk.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者:
public class Consumer implements Runnable {
private Milk milk;
public Consumer(Milk milk){
this.milk = milk;
}
public void run() {
while (true){
try{
milk.get();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
缓冲区:
public class Milk {
private int milk;
private boolean state=false;//有奶:true;无奶:false
public synchronized void put(int i) throws InterruptedException {
if (state){
wait();
}
this.milk = i;
System.out.println("送奶工送入第"+i+"瓶奶");
state = true;
notifyAll();
}
public synchronized void get() throws InterruptedException {
if (!state){
wait();
}
System.out.println("消费者喝了第"+milk+"瓶奶");
state = false;
notifyAll();
}
}
Milk 中属性:milk 是Producer和Consumer线程共用的数据,并且对共用的数据操作是非原子性的。因此对这样的资源的操作要加锁。所以在Milk中对milk的各种操作方法要加锁。
方法二:Lock锁
public class Ticket implements Runnable {
private int ticket = 100;
private Lock lock = new ReentrantLock();
public void run() {
while (true){
lock.lock();
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"完成买票,余票为"+--ticket);
}
lock.unlock();
}
}
}
测试类:
public class TicketTest {
public static void main(String[] args) {
Runnable ticket = new Ticket();
Thread thread1 = new Thread(ticket,"窗口一");
Thread thread2 = new Thread(ticket,"窗口二");
Thread thread3 = new Thread(ticket,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
三个线程共享Ticket中的ticket变量,Ticket中对ticket的操作不是原子性的因此需要加锁。
线程安全类:
StringBuffer、vector、HashTable 是线程安全类。一般不使用Vector,使用Collections类的静态方法直接将线程不全的集合转为线程安全的集合。