黑马程序员——多线程二

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

多线程二

一、线程安全问题
线程安全问题出现的原因?
1、有多个线程存在
2、多个线程操作同一个资源空间
3、操作资源的语句有多句

二、线程安全问题的解决方案
通过同步可以解决线程安全问题。

三、线程同步的方法
1、同步代码块
2、同步函数
3、Lock锁

四、代码练习
1、同步代码块的练习
package it.heima.thread;

public class SycronizedDemo {
	public static void main(String[] args) {
		Student student = new Student();
		Thread thread = new Thread(student, "张三");
		Thread thread2 = new Thread(student, "李四");
		thread.start();
		thread2.start();
	}
}

class Student implements Runnable {
	Object obj=new Object();
	@Override
	public void run() {
		while (apple > 1) {
			synchronized (obj) {<span style="white-space:pre">	</span>//同一个锁对下昂
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "吃了第" + apple-- + "个苹果");
				
			}
		}
	}

	int apple;<span style="white-space:pre">	</span>

	public Student() {
		apple = 100;
	}

}
通过sycronized关键字将要同步的代码括起来,并通过锁对象锁住代码块。
锁对象可以是任意对象,但是要同一把锁才能锁住多个线程。sycronized(new Object())有多个"锁",所以锁不住。

2、同步函数
同步函数分为2个部分:静态同步函数与非静态同步函数
同步函数的锁对象是固定的,不能修改,非静态同步函数的锁对象是this,静态同步函数的锁对象是class对象。

代码测试:
package it.heima.thread;

public class SycronizedMethodDemo {
	public static void main(String[] args) {
		ThreadDemo demo = new ThreadDemo();

		Thread employee1 = new Thread(demo, "窗口1");
		Thread employee2 = new Thread(demo, "窗口2");
		Thread employee3 = new Thread(demo, "窗口3");

		employee1.start();
		employee2.start();
		employee3.start();
	}
}

class ThreadDemo implements Runnable {
	static int tickets = 100;
	int x = 1;

	public ThreadDemo() {

	}

	/*public synchronized void saltTicket() {
			if(tickets>0)
				System.out.println(Thread.currentThread().getName() + "卖了第" + tickets + "张票");
			tickets--;
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}*/
	public static synchronized void saltTicket() {
		if(tickets>0)
			System.out.println(Thread.currentThread().getName() + "卖了第" + tickets + "张票");
		tickets--;
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
}

	@Override
	public void run() {
		while (tickets>0) {
			if (x % 2 == 0) {
				synchronized (ThreadDemo.class) {
					if (tickets > 0) {
						System.out.println(Thread.currentThread().getName() + "卖了第" + tickets + "张票");
						tickets--;
						try {
							Thread.sleep(20);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			} else {
				saltTicket();//非静态同步函数和静态同步函数测试
				
			}

			x++;
		}
		// super.run();
	}

}
结合同步代码块与同步函数一起,发现将同步代码块的锁对象设置为this时,可以锁住非静态同步函数。锁对象设置为class时,可以锁住静态同步函数。

3、Lock
在学习Lock锁之前,先学习多线程的几个方法:
notify()、wait()、notifyAll();
这三个方法都是依赖锁对象的监听器作为标志创建的线程池存在的。
代码演示:
package it.heima.thread;

/*
 * 为了让生产者和消费者操作同一个对象,故而使他们操作同一个对象。就在他们内部维护一个produce.
 * */
public class NotifyDemo {
	public static void main(String[] args) {
		Produce p=new Produce();
		Customer c=new Customer(p);
		Producer pp=new Producer(p);
		
		c.start();
		pp.start();
	}
}

class Produce {
	int price;
	String name;
	boolean flag;
}

class Customer extends Thread {
	private Produce p;

	public Customer(Produce p) {
		// TODO Auto-generated constructor stub
		this.p = p;
	}

	@Override
	public void run() {
		buy();
	}

	public void buy() {
		while (true) {
			synchronized ("线程池") {
				if (p.flag == true) {
					System.out.println("顾客花了" + p.price + "买了" + p.name);
					p.flag = false;
					"线程池".notify();
				}else{
					try {
						"线程池".wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

class Producer extends Thread {
	private Produce p;
	private int x = 1;

	public Producer(Produce p) {
		this.p = p;
	}

	@Override
	public void run() {
		creat();
	}

	public void creat() {
		while (true) {
			synchronized ("线程池") {
				if (p.flag == false) {
					if (x % 2 == 0) {
						p.name = "自行车";
						p.price = 300;
						System.out.println("生产者生产了:" + p.name + "   价格:" + p.price);
						"线程池".notify();
						p.flag = true;
					} else {
						p.name = "BMW";
						p.price = 10000000;
						System.out.println("生产者生产了:" + p.name + "   价格:" + p.price);
						"线程池".notify();
						p.flag = true;
					}
					x++;
				} else {
					try {
						"线程池".wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

}
出现异常:当调用notify()方法的对象不是锁对象时,会出现异常:
java.lang.IllegalMonitorStateException  
违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。

Lock是一个接口,实现了更为灵活的同步功能。我们用他的子实现类完成同步需求。


Sychronized和Lock的区别
1、Sychroniezed在获取锁与释放锁的过程是隐式的,Lock是显式的。
2、Sychroniezed的监听器是隐式的,Lock是显式的,可以一个lock由多个监听器

Lock的方法:
lock()
unlock()
newCondition()

Condition()的方法:
signal();
await();
signalAll()

这些方法与Object的方法对应
sycronized()------------>lock()和unlock()
wait()------------->await()
notify()------------->signal();
notifyAll()------------->signalAll()

代码实现:
package it.heima.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 为了让生产者和消费者操作同一个对象,故而使他们操作同一个对象。就在他们内部维护一个produce.
 * */
public class NotifyDemo {
	public static void main(String[] args) {
		Produce p=new Produce();
		ReentrantLock r=new ReentrantLock();
		Condition cCondition=r.newCondition();
		Condition pCondition=r.newCondition();
		
		Customer c=new Customer(p,r,cCondition,pCondition);
		Producer pp=new Producer(p,r,cCondition,pCondition);
		
		
		c.start();
		pp.start();
	}
}

class Produce {
	int price;
	String name;
	boolean flag;
}

class Customer extends Thread {
	private Produce p;
	ReentrantLock r;
	Condition cCondition;
	Condition pCondition;
	
	/*public Customer(Produce p) {
		// TODO Auto-generated constructor stub
		this.p = p;
	}*/
	public Customer(Produce p,ReentrantLock r,Condition cCondition,Condition pCondition) {
		// TODO Auto-generated constructor stub
		this.p = p;
		this.r=r;
		this.cCondition=cCondition;
		this.pCondition=pCondition;
	}
	
	
	@Override
	public void run() {
		buy();
	}

	public void buy() {
		while (true) {
		//	synchronized ("线程池") {
			r.lock();
				if (p.flag == true) {
					System.out.println("顾客花了" + p.price + "买了" + p.name);
					p.flag = false;
					//"线程池".notify();
					pCondition.signal();
				}else{
					try {
						//"线程池".wait();
						cCondition.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				r.unlock();
		//	}
		}
	}
}

class Producer extends Thread {
	private Produce p;
	private int x = 1;
	ReentrantLock r;
	Condition cCondition;
	Condition pCondition;
	
	/*public Producer(Produce p) {
		this.p = p;
	}*/
	public Producer(Produce p,ReentrantLock r,Condition cCondition,Condition pCondition) {
		// TODO Auto-generated constructor stub
		this.p = p;
		this.r=r;
		this.cCondition=cCondition;
		this.pCondition=pCondition;
	}

	@Override
	public void run() {
		creat();
	}

	public void creat() {
		while (true) {
			//synchronized ("线程池") {
			r.lock();
				if (p.flag == false) {
					if (x % 2 == 0) {
						p.name = "自行车";
						p.price = 300;
						System.out.println("生产者生产了:" + p.name + "   价格:" + p.price);
					//	"线程池".notify();
						p.flag = true;
						cCondition.signal();
					} else {
						p.name = "BMW";
						p.price = 10000000;
						System.out.println("生产者生产了:" + p.name + "   价格:" + p.price);
					//	"线程池".notify();
						p.flag = true;
						cCondition.signal();
					}
					x++;
				} else {
					try {
					//	"线程池".wait();
						pCondition.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				
				}
			r.unlock();
		//	}
		}
	}

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值