1.生产者消费者的问题
public class Test {
public static void main(String[] args) {
Queue q=new Queue();
new Thread(new 生产者(q)).start();
new Thread(new 消费者(q)).start();
}
}
//缓冲区
class Queue{
String name;
String tool;
}
class 生产者 implements Runnable {
生产者(Queue q){
this.q=q;
}
Queue q;
public void run() {
int i=0;
while(true){
synchronized(q)
{
if(i%2==0){
q.name="鸡蛋";
try{Thread.sleep(10);}catch(Exception ex){}
q.tool="平底锅";
}
else{
q.name="核桃";
q.tool="锤子";
}
i=(i+1)%2; //让i 进行切换
}
}
}
}
class 消费者 implements Runnable {
Queue q;
消费者(Queue q){
this.q=q;
}
public void run() {
while(true){
synchronized(q)
{
System.out.print(q.name);
try{Thread.sleep(10);}catch(Exception ex){}
System.out.println("-" + q.tool);
}
}
}
}
对于上面的程序,输出结果为
鸡蛋 -平底锅
鸡蛋 -平底锅
鸡蛋 -平底锅
核桃 -锤子
核桃 -锤子
核桃 -锤子
核桃 -锤子
鸡蛋 -平底锅
鸡蛋 -平底锅
鸡蛋 -平底锅
要完成两个线程的协作,就要用到线程的通迅
2.线程间的通信
--wait() 告诉当前线程放弃锁(对象监视器),进行等待,直到接收到在这个锁上的notify通知时继续
--notify() 通知等待锁的线程,开始运行
--notifyAll 通知等待锁的所有线程,开始运行
重写的生产者消费者程序
public class Test {
public static void main(String[] args) {
Queue q=new Queue();
new Thread(new 生产者(q)).start();
new Thread(new 消费者(q)).start();
}
}
//缓冲区
class Queue extends Object{
String name;
String tool;
boolean 有东西=false;
}
class 生产者 implements Runnable {
生产者(Queue q){
this.q=q;
}
Queue q;
public void run() {
int i=0;
while(true){
synchronized(q)
{
if(q.有东西==true){
try{q.wait();}catch(Exception ex){}
}
if(i%2==0){
q.name="鸡蛋";
try{Thread.sleep(10);}catch(Exception ex){}
q.tool="平底锅";
}
else{
q.name="核桃";
q.tool="锤子";
}
q.有东西=true;
q.notify(); //通知消费者过来取
i=(i+1)%2; //让i 进行切换
}
}
}
}
class 消费者 implements Runnable {
Queue q;
消费者(Queue q){
this.q=q;
}
public void run() {
while(true){
synchronized(q)
{
if(q.有东西==false){
try{q.wait();}catch(Exception ex){}
}
System.out.print(q.name);
try{Thread.sleep(10);}catch(Exception ex){}
System.out.println("-" + q.tool);
q.有东西=false;
q.notify();
}
}
}
}
3.生产者消费者程序的优化
public class Test {
public static void main(String[] args) {
Queue q=new Queue();
new Thread(new 生产者(q)).start();
new Thread(new 消费者(q)).start();
}
}
class Queue{ // Queue 是一个线程安全的类
String name;
String tool;
boolean 有东西=false;
//放东西
synchronized public void put(String name,String tool){
if(this.有东西){
try{this.wait();}catch(Exception ex){}
}
this.name=name;
this.tool=tool;
this.有东西=true;
this.notify();
}
//取东西
synchronized public void get(){
if(this.有东西==false){
try{this.wait();}catch(Exception ex){}
}
System.out.print(this.name);
System.out.println("-" +this.tool);
this.有东西=false;
this.notify();
}
}
class 生产者 implements Runnable{
生产者(Queue q){
this.q=q;
}
Queue q;
public void run() {
int i=0;
while(true){
if(i%2==0){
q.put("鸡蛋", "平底锅");
}
else{
q.put("核桃", "锤子");
}
i=(i+1)%2;
}
}
}
class 消费者 implements Runnable{
消费者(Queue q){
this.q=q;
}
Queue q;
public void run() {
while(true){
q.get();
}
}
}
4.线程的终止
1) 设定线程的结束标志
2) Interupt();
3) stop(); //废弃,容易引起死锁
class Test{
public static void main(String[] args) {
EmpThread empThread=new EmpThread();
new Thread(empThread).start();
for (int i = 0; i < 100; i++) {
if(i==90){
empThread.stopMe(true);
}
System.out.println("领导正在讲第话 第"+i +"句");
}
}
}
class EmpThread implements Runnable
{
private boolean isStop;
public void stopMe(boolean isStop){
this.isStop=isStop;
}
public void run(){
while(isStop==false){
System.out.println("员工在鼓掌..");
}
}
}
5.后台线程
方法
public final void setDaemon(boolean on)
如果传ture, 则将一个线程,设置成后台线程 , 要在线程开启之前调用
后台线程 (也叫守护线程,精灵线程)
系统中可以同时运行着前台线程,和后台线程,
当系统中没有前台线程运行的时候,java虚拟机会退出
6.JDK 1.5 以后的 java.util.concurrent.locks
自JDK1.5以为,Java提供了java.util.concurrent这个并发包,在它的子包locks中,
提供了一系列关于锁的抽象的类。主要有两种锁
ReentrantLock //一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大
ReentrantReadWriteLock //支持与 ReentrantLock 类似语义的 ReadWriteLock 实现。 (ReadWriteLock //维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。)
而其他的两个类,都是“辅助”类,
如AbstractQueuedSynchronizer就是一个用于实现特殊规则锁的抽象类,ReentrantLock和ReentrantReadWriteLock内部都有一个继承了该抽象类的内部类,
用于实现特定锁的功能。下文主要介绍:ReentrantLock和ReentrantReadWriteLock
附: Reentrant 这个单词,是可重入的意思
// 可重入的锁ReentrantLock
使用ReentrantLock锁最简单的一个例子:
Lock lock = new ReentrantLock();
try {
lock.lcok();
// do something
} finally {
lock.unlock();
}
上面这段代码,首先创建了一个lock,然后调用它的lock()方法,开启锁定,在最后调用它的unlock()解除锁定。
值得注意的时,一般在使用锁时,都应该按上面的风格书写代码,即lock.unlock()最好放在finally块,这样可以防止,
执行do something时发生异常后,导致锁永远无法被释放。
Lock 和 synchronized 的区别:
1) Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等
2) synchronized 是Java 语言层面的,是内置的关键字;Lock 则是JDK 5 中出现的一个包,在使用时,synchronized
同步的代码块可以由JVM自动释放;Lock 需要程序员在finally块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。
Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。
这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义
3) synchronized是比较古老的实现机制,设计较早,有一些功能上的限制:
== 它无法中断一个正在等候获得锁的线程
== 也无法通过投票得到锁,如果不想等下去,也就没法得到锁。
== 同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Test{
public static void main(String[] args) throws InterruptedException {
SaleThread s =new SaleThread();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
}
}
class SaleThread implements Runnable{
int ticket=100;
Lock lock=new ReentrantLock(); //注意,多个线程要共用同一个锁
public void run(){
while(true){
lock.lock();
try{
if(ticket>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("线程 "+Thread.currentThread().getName()+"正在卖第"+ticket-- +"张票" );
}
else{
break;
}
}
catch(Exception ex){
ex.printStackTrace();
}
finally{
lock.unlock();
}
}
}
}