线程间通讯:
wait和sleep的区别:其实就是多个线程在操作同一个资源,操作的动作不同
wait:释放cpu执行权,释放同步中的锁。sleep:释放cpu执行权,不释放同步中的锁。
/**
线程间通信示例
需求:
输入一次信息,打印一次信息
分析:
需要两个线程
一个是输入线程
一个是打印线程
有一个共享资源:用来存储信息和获取信息
思路:
定义三个类
类Rec:共享的资源
类Input:具备输入信息的功能
类Output:具备打印信息的功能
*/
class Rec//定义一个对象
{
String name;
String sex;
boolean flag = false; //定义一个标记,用于控制线程输入一次,打印一次
}
class Input implements Runnable//具备输入功能的对象
{
private Rec r;
Input(Rec r)//传入要操作的对象
{
this.r = r;
}
public void run()//输入方法
{
int x = 0;
while (true)
{
while (r.flag)
{
try{r.wait();}catch(Exception e){}
}
synchronized(r)
{
if (x == 0)
{
r.name = "张三";//输入信息
r.sex = "++++++男";
}else
{
r.name = "李四";
r.sex = "女";
}
x = (x+1)%2;//此代码的结果不是0就是1,利用这个特性来控制信息的交替输入,使用boolean数据类型也可以实现
r.flag = true;//该语句的结果控制信息输入一次后,就进入冻结状态
r.notify();//唤醒线程池中冻结的线程
}
}
}
}
class Output implements Runnable
{
private Rec r;
Output(Rec r)
{
this.r = r;
}
public void run()
{
while (true)
{
synchronized(r)
{
while (!r.flag)//要让线程交替执行,就必须使其满足的条件互斥,所以这里条件是!r.flag
{
try{r.wait();}catch(Exception e){}
}
System.out.println(r.name+"++++++"+r.sex);
r.flag = false;//执行完毕后修改值,让该线程执行wait()进入冻结状态,并跳转到输入功能
r.notify();
}
}
}
}
class InputOutputDemo
{
public static void main(String[] args)
{
Rec r = new Rec();//建立共享资源对象
Input in = new Input(r);//将共享资源传入具备输入功能的对象中
Output ou = new Output(r);//将共享资源传入具备输出功能的对象中
Thread t1 = new Thread(in);//建立线程
Thread t2 = new Thread(ou);
t1.start();//开启线程
t2.start();
}
}
wait():
notify();
notifyAll();
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
notify();
notifyAll();
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程的锁,只有同一个锁上的等待线程,可以被同一个锁(notify())唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒的线程必须持有相同的锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
生产消费者案例:
/**
需求:
多条线程生产商品,多条线程销售商品,且保证生产一个销售一个
分析:
定义多条线程生产商品
定义多条线程销售商品
定义一个生产者类,该类具备生产商品的功能
定义一个消费者,该类具备销售商品的功能
该程序中的共享资源是商品,商品可以被生产和销售
*/
class Res
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)//将生产功能封装起来
{
/*
上一个案例中,是一个线程负责输入,一个线程负责输出
这个案例是,多个线程负责输入,多个线程负责输出
这里的while不可以使用if代替,否则会出现,生产两个商品只卖出了一个商品,或者是生产一个商品却卖出了两次
那是因为,当线程冻结后,被唤醒的线程直接执行wait后的语句,而不用判断flag标记而造成的
此处改为while语句就可以避免这样的情况发生,被唤醒的线程将会再次判断flash标记
这是本演示案例的重点之一
*/
while (flag)
{
try{wait();}catch(Exception e){}
}
this.name = name+"----"+count++;//这句代码不能放在while语句前面,否则会出现先消费后生产的怪异现象
System.out.println(Thread.currentThread().getName()+"-------生产者-----------"+this.name);
flag = true;
/*
本案例重点二
该出使用notifyAll是为了唤醒除本方线程的其他线程
如果使用notify,将有可能一直唤醒本方线程,而本方线程的flag一直是false,下面的语句得不到执行,
将会出现线程停止不动的现象
*/
notifyAll();
}
public synchronized void out()
{
while (!flag)
{
try{this.wait();}catch(Exception e){}
}
System.out.println(Thread.currentThread().getName()+"--消费者--"+this.name);
flag = false;
notifyAll();
}
}
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while (true)
{
r.set("商品");//直接调用set方法
}
}
}
class Customer implements Runnable
{
private Res r;
Customer(Res r)
{
this.r = r;
}
public void run()
{
while (true)
{
r.out();
}
}
}
class ProducerCustomerDemo
{
public static void main(String[] args)
{
Res r = new Res();
Producer pro = new Producer(r);
Customer cus = new Customer(r);
Thread t1 = new Thread(pro);//开启两个生产者线程
Thread t2 = new Thread(pro);
Thread t3 = new Thread(cus);//开启两个消费者线程
Thread t4 = new Thread(cus);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
可不可以只唤醒对方呢?有的。
JAVA1.5版本之后出现了一个新的包java.util.concurrent.locks
该包中提供了condition接口和lock接口
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换成了Condition对象。
该对象可以对Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object中的 wait notify notifyAll
await();
signal();
signalAll();
将Object中的wait,notify notifyAll,替换成了Condition对象。
该对象可以对Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object中的 wait notify notifyAll
await();
signal();
signalAll();
生产消费者案例代码升级:
/**
使用1.5版本出现的lock和condition
对程序进行升级优化
*/
import java.util.concurrent.locks.*;
class Res
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();//定义一个锁
private Condition con_pro = lock.newCondition(); //创建一个生产者condition
private Condition con_cus = lock.newCondition();//创建一个消费者condition
public void set(String name) throws InterruptedException
{
lock.lock();//获取锁,也可称为加锁
try
{
while (flag)
{
con_pro.await()//冻结pro中线程
}
this.name = name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"-------生产者-----------"+this.name);
flag = true;
con_cus.signal();//唤醒消费者的线程
}
finally
{
lock.unlock;//释放锁,一定要执行
}
}
public void out() throws InterruptedException
{
lock.lock();//加锁
try
{
while (!flag)
{
con_cus.await();//冻结本方的线程
}
System.out.println(Thread.currentThread().getName()+"--消费者--"+this.name);
flag = false;
con_pro.signal();//唤醒对方线程
}
finally
{
lock.unlock();//释放锁
}
}
}
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while (true)
{
r.set("商品");//直接调用set方法
}
}
}
class Customer implements Runnable
{
private Res r;
Customer(Res r)
{
this.r = r;
}
public void run()
{
while (true)
{
r.out();
}
}
}
class ProducerCustomerDemo
{
public static void main(String[] args)
{
Res r = new Res();
Producer pro = new Producer(r);
Customer cus = new Customer(r);
Thread t1 = new Thread(pro);//开启两个生产者线程
Thread t2 = new Thread(pro);
Thread t3 = new Thread(cus);//开启两个消费者线程
Thread t4 = new Thread(cus);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
控制线程(停止线程,加入线程,暂停线程,控制线程的优先级)
特殊情况:只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
处理方法:
当没有指定的方式让冻结的线程恢复到运行状态时,如果需要对冻结状态进行清除,
强制让线程恢复到运行状态中来。这时 可以使用Thread类中的interrupt()方法来清除冻结,
当没有指定的方式让冻结的线程恢复到运行状态时,如果需要对冻结状态进行清除,
强制让线程恢复到运行状态中来。这时 可以使用Thread类中的interrupt()方法来清除冻结,
这样就可以继续判断操作标记让线程结束。
代码示例:
class StopThread implements Runnable
{
private boolean flag =true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)//处理异常
{
System.out.println(Thread.currentThread().getName()+"....Excepiton");
flag = false;//改变标记,让进入运行状态的线程开始执行
}
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;//控制循环的标记
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
t1.interrupt();//强制将冻结状态的线程终止,并让其进入运行状态
t2.interrupt();//调用此方法会出现InterruptException异常
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
守护线程(后台线程)
守护线程的特点:当前台线程执行完毕,只剩下守护线程时,守护线程自动结束
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
理解:调用了join()方法的线程,抢夺了CPU的执行权,这时,其他线程将会被冻结,该线程join结束,其他线程恢复到运行状态
应用:多个线程运行时,临时加入一个线程,并让其执行完。
优先级(Priority)
优先级的三种级别
MAX_PRIORITY
MIN_PRIORITY
NORM_PRIORITY(主线程的默认级别)
改变优先级的方法:Thread.setPriority(MAX_PRIORITY)
优先级的大小,可以控制线程获取执行权的概率;优先级越大,获取执行权的概率越大,优先级越小,获取执行权的概率越小。
yield()
暂停(释放执行权)当前正在执行的线程对象,并执行其他线程。
线程中使用到的方法
void interrupt()
中断线程。
中断线程。
java.util.concurrent
|--lock
|--lock():获取锁
|--unlock():释放锁
|--condition
|--await():等待
|--signal():唤醒一个等待线程
|--signalAll():唤醒所有等待线程