多线程__下


线程间通讯:
其实就是多个线程在操作同一个资源,
操作的动作不同

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())唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒的线程必须持有相同的锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义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();
	}
}

这个程序有一个bug。就是每次notifyAll。都会唤醒本方。
可不可以只唤醒对方呢?有的。

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();

生产消费者案例代码升级:
/**
使用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()方法来清除冻结,
这样就可以继续判断操作标记让线程结束。

代码示例:
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()方法的线程,抢夺了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():唤醒所有等待线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值