一 多线程通讯
- 多线程通讯的意思其实很简单,就是不同于以往的两个线程做同样的事情操作同一个对象——比如day11的火车售票,而是多个线程执行对于同一个对象执行不同的事情,比如说今天的对同一个对象中的数据,一个线程负责输入,一个线程负责输出;
- 多线程通讯由于不同线程做的事情不同,因此需要多个对象实现Runnable接口并重写run方法;
- 多线程应当注意的仍然是安全问题,因此在需要同步的地方慎重判断,本例由于操作的还是同一个对象中的数据,因此两个run方法所在的Input和Output的同步函数可以用操作对象当做锁,从而实现同一把锁,避免安全隐患;
- 今天的代码中要实现的是输入数据,而后立刻输出,因此引入了flag进行判断输入的数据是否已经输出,以及wait方法和notify方法用于暂停/唤醒——输入/输出线程;
- 如果数据已经输出则输入数据,如果数据未输出则暂停当前的输入线程,唤醒输出线程,转到输出线程进行输出,在输出线程中判断数据是否已经输出,如果没有,则输出数据,如果已经输出,则暂停输出线程,唤醒输入线程进行输入;
- 以上的话过于繁琐,还是看两个版本的代码来的快。。。
/*
输入一个姓名和性别,然后用另一个线程输出
*/
class Resource{
String name;
String sex;
boolean flag = false; //用来作为判定输入是否输出的标识
}
class Input implements Runnable{
private Resource res;
Input(Resource res){
this.res = res;
}
public void run(){
int x = 0;
while(true){
//两个线程用同一个对象锁
synchronized(res){
if (res.flag)
try{
res.wait();
}catch(InterruptedException e){
}
if(x==0){
res.name = "张三";
res.sex = "男";
}else {
res.name = "WWW";
res.sex = "woman";
}
x = (x+1)%2;
res.flag = true;
res.notify();
}
}
}
}
class Output implements Runnable{
private Resource res;
Output(Resource res){
this.res = res;
}
public void run(){
while(true){
//两个线程用同一个对象锁
synchronized(res){
if(!res.flag)
try{
res.wait();
}catch(InterruptedException e){
}
System.out.println(res.name+"......"+res.sex);
res.flag = false;
res.notify();
}
}
}
}
class ThreadCommunicationDemo{
public static void main(String[] args){
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
下面的是优化版本
/**
用一个线程输入姓名和性别,用另一个线程输出姓名和性别的
优化版本!
@author zck;
@version V1.1;
*/
class Resource{
private String name;
private String sex;
private boolean flag = false; //用来作为判定输入是否已经输出的标识
public synchronized void setNameSex(String name,String sex){
if (flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
public synchronized void getNameSex(){
if (!flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
System.out.println(this.name+"......"+this.sex);
flag = false;
notify();
}
}
class Input implements Runnable{
private Resource res;
Input(Resource res){
this.res = res;
}
public void run(){
int x = 0;
while(true){
if(x==0){
res.setNameSex("张三","男");
}else {
res.setNameSex("WWW","woman");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Resource res;
Output(Resource res){
this.res = res;
}
public void run(){
while(true){
res.getNameSex();
}
}
}
class ThreadCommunicationDemo2{ //优化版本
public static void main(String[] args){
Resource r = new Resource();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
二 多线程通讯——生产者消费者
- 在多个线程运行时,有多个生产者,多个消费者;
在屏幕上打印输出时,有时候会出现生产两次却只消费了一次的情况,这与预期的生产一次消费一次不符合,这种安全隐患来自于flag的if判断,因为只判定一次,当生产线程1通过if判断时,执行完wait语句就失去了执行权,然后接下来消费线程1执行,然后生产线程2执行(因为此时flag是false),当生产线程2执行完时,flag变成了true,但是由于生产线程1已经在之前中通过了if判断,当生产线程1获得执行权时,就会再生产一次;
打印结果:
生产商品1
消费商品1
生产商品2
生产商品3
消费商品3
改进方法就是将if换成while判断还有一种安全隐患就是notify叫醒的线程池里本类线程,就会出现叫醒生产线程1叫醒生产线程2,然后所有线程都挂了(都进入wait状态);处理方式就是改notify为notifyAll,干脆全叫醒,就不会出现所有线程一起挂在那儿的情况了;
class ProducerConsumerDemo{
public static void main(String[] args){
ProducerConsumer pc = new ProducerConsumer();
Producer pro = new Producer(pc);
Consumer con = new Consumer(pc);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class ProducerConsumer{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void setName(String name){
while(true){
while(flag){
try{
wait();
}catch(InterruptedException e){
}
}
this.name = name+"..."+count++;
System.out.println(Thread.currentThread().getName()+"---生产---"+this.name);
flag = true;
this.notifyAll();
}
}
public synchronized void getName(){
while(true){
while(!flag){
try{
wait();
}catch(InterruptedException e){
}
}
System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name);
flag = false;
this.notifyAll();
}
}
}
class Producer implements Runnable{
ProducerConsumer pc;
Producer(ProducerConsumer pc){
this.pc = pc;
}
public void run(){
pc.setName("商品");
}
}
class Consumer implements Runnable{
ProducerConsumer pc;
Consumer(ProducerConsumer pc){
this.pc = pc;
}
public void run(){
pc.getName();
}
}
下面是java5.0改进版本
1. 引入了lock代替synchronized,condition对象的await代替wait,signal和signalAll代替notify和notifyAll;
import java.util.concurrent.locks.*;
class ProducerConsumerDemo2{
public static void main(String[] args){
ProducerConsumer pc = new ProducerConsumer();
Producer pro = new Producer(pc);
Consumer con = new Consumer(pc);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class ProducerConsumer{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void setName(String name) throws InterruptedException{
lock.lock();
try{
while(flag){
condition_pro.await();
}
this.name = name+"..."+count++;
System.out.println(Thread.currentThread().getName()+"---生产---"+this.name);
flag = true;
condition_con.signalAll();
}finally{
lock.unlock();
}
}
public void getName() throws InterruptedException{
lock.lock();
try{
while(!flag){
condition_con.await();
}
System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name);
flag = false;
condition_pro.signalAll();
}finally{
lock.unlock();
}
}
}
class Producer implements Runnable{
ProducerConsumer pc;
Producer(ProducerConsumer pc){
this.pc = pc;
}
public void run(){
while(true){
try{
pc.setName("商品");
}catch(InterruptedException e){
}
}
}
}
class Consumer implements Runnable{
ProducerConsumer pc;
Consumer(ProducerConsumer pc){
this.pc = pc;
}
public void run(){
while(true){
try{
pc.getName();
}catch(InterruptedException e){
}
}
}
}
三 中断线程
- 有时候线程处于冻结状态后无法正常读取唤醒标记,这时候就需要外力强制中断线程的冻结状态;
- Thread类就提供了interrupt方法,强制唤醒线程(好比把被催眠的人一砖头砸醒),但是这样会导致wait或者sleep抛出异常(因为被砖头砸醒不比正常唤醒,会受伤的,,);
class InterruptThreadDemo{
public static void main(String[] args){
int num = 0;
TestThread tt = new TestThread();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start();
while(true){
if(num++ == 60){
//tt.changeFlag();
//强制唤醒了t1和t2线程
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
}
}
class TestThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+"InterruptedException");
//结束循环实则为结束进程
flag = false;
}
System.out.println(Thread.currentThread().getName()+"run.....");
}
}
public void changeFlag(){
flag = false;
}
}
四 守护线程
- 可以通过Thread 的setDaemon方法将一些线程设置为守护线程;
- 守护线程的设置应当在线程执行前;
- 守护线程可视为后台线程,当所有前台线程都结束时,守护线程自动结束;
- 这意味着守护线程即使被wait了,也没必要唤醒后再结束,只要前台线程跑完即可;
class InterruptThreadDemo{
public static void main(String[] args){
int num = 0;
TestThread tt = new TestThread();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
while(true){
if(num++ == 60){
//tt.changeFlag();
//强制唤醒了t1和t2线程
//t1.interrupt();
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
}
}
class TestThread implements Runnable{
private boolean flag = true;
public void run(){
while(flag){
System.out.println(Thread.currentThread().getName()+"run.....");
}
}
public void changeFlag(){
flag = false;
}
}
五 join、yield方法和优先级
- join方法实则为暂停当前线程,优先执行调用join方法的线程,直到调用join方法的线程结束后再执行当前线程;
- toString方法可以查看线程名称,优先级,以及所在线程组;
- 优先级为1——10,代表被cpu执行的概率;
- 通过setPriority(Thread.MAX/MIN/NORM_PRIORITY)设置三个不同的优先级,对应10/1/5;
- yield方法是暂停当前线程,执行其他线程;
class JoinDemo{
public static void main(String[] args) throws InterruptedException{
Join jo = new Join();
Thread t1 = new Thread(jo);
Thread t2 = new Thread(jo);
t1.start();
t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
//t1.join();
for(int x = 0;x<80;x++){
System.out.println(Thread.currentThread().toString()+"....."+x);
}
}
}
class Join implements Runnable{
public void run(){
for (int x = 0;x<70;x++){
System.out.println(Thread.currentThread().toString()+"..."+x);
//Thread.yield();
}
}
}