线程生产者和消费者
说明:
1.共享同一个数据。
2.有一个线程负责向该共享数据存放数据。—生产者
3.另一个线程负责从该数据空间中取出数据。—消费者
为什么生产者不直接把生产的数据交给消费者而非要一个共享区来完成这样的操作呢?
其实这样是为了解除耦合关系,即如果其中一方出现问题并不会影响另外一方。说具体点就是若不这样做,修改了消费者的类也得修改生产者的类,修改了生产者的类也得修改消费者的类。
最基本代码:
//生产者
class Producer implements Runnable{
private ShareResource share=null;//告诉共享区的对象是哪一个
public Producer(ShareResource share){//此构造方法用于在生产者中创建共享区的对象
this.share=share;
}
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
share.push(40,"李四");
}else {
share.push(30, "张三");
}
}
}
}
//消费者
class consumer implements Runnable{
private ShareResource share=null;
public consumer(ShareResource share){
this.share=share;
}
public void run() {
for(int i=0;i<1000;i++) {
share.popup();
}
}
}
//共享区
class ShareResource {
private int age;
private String name;
public void push(int age, String name) {
this.age = age;
this.name = name;
}
public void popup() {
System.out.println("年龄:" + this.age + " " + "名字:" + this.name);
}
}
//测试类
class App{
public static void main(String[] args){
ShareResource share=new ShareResource();//创建共享区
new Thread(new Producer(share)).start();//创建并启动生产者线程
new Thread(new consumer(share)).start();//创建并启动消费者线程
}
}
上述代码从思路上并没有什么问题,但是CPU调度并没有让我们那么轻松的完成,CPU调度所带来的问题主要有两个,一个是会导致紊乱的问题,另外还会导致生产一个却消费多个的情况,我们主意来解决。
解决紊乱的问题
升级代码:
//生产者
class Producer implements Runnable{
private ShareResource share=null;
public Producer(ShareResource share){
this.share=share;
}
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
share.push(40,"李四");
}else {
share.push(30, "张三");
}
}
}
}
//消费者
class consumer implements Runnable{
private ShareResource share=null;
public consumer(ShareResource share){
this.share=share;
}
public void run() {
for(int i=0;i<1000;i++) {
share.popup();
}
}
}
//共享区
class ShareResource {
private int age;
private String name;
synchronized public void push(int age, String name) {//synchronized锁住当前对象,我被锁住了其他想用此对象的都不不会执行,这很重要
this.age = age;
this.name = name;
}
synchronized public void popup() {
System.out.println("年龄:" + this.age + " " + "名字:" + this.name);
}
}
//测试类
class App{
public static void main(String[] args){
ShareResource share=new ShareResource();//创建共享区
new Thread(new Producer(share)).start();//创建并启动生产者线程
new Thread(new consumer(share)).start();//创建并启动消费者线程
}
}
其实解决紊乱的问题很简单,只需要在push与popup方法前加synchronized修饰即可
解决生产一个消费多个的问题
最终代码:
//生产者
class Producer implements Runnable{
private ShareResource share=null;
public Producer(ShareResource share){
this.share=share;
}
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
share.push(40,"李四");
}else {
share.push(30, "张三");
}
}
}
}
//消费者
class consumer implements Runnable{
private ShareResource share=null;
public consumer(ShareResource share){
this.share=share;
}
public void run() {
for(int i=0;i<1000;i++) {
share.popup();
}
}
}
//共享区
class ShareResource {
private int age;
private String name;
private boolean isEmpty=true;
synchronized public void push(int age, String name) {//synchronized锁住当前对象,我被锁住了其他想用此对象的都不不会执行,这很重要
while(!isEmpty){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.age = age;
try { //当加上sleep时出现紊乱出现张三40,李四30的情况 解决:synchronized代码块,方法,Lock机制
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
isEmpty=false;
this.notify();//唤醒当前对象的另外一个线程
}
synchronized public void popup() {
while(isEmpty){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("年龄:" + this.age + " " + "名字:" + this.name);
isEmpty=true;
this.notify();
}
}
//测试类
class App{
public static void main(String[] args){
ShareResource share=new ShareResource();//创建共享区
new Thread(new Producer(share)).start();//创建并启动生产者线程
new Thread(new consumer(share)).start();//创建并启动消费者线程
}
}
解决这个问题主要使用了wait()与notify()方法来启动和关闭线程,并创建一个布尔常量来控制哪个线程执行。
死锁
死锁就是多个线程都在等待别的锁的释放,第一个线程在等待另外一个线程释放锁,另外一个锁又在等待第一个线程释放锁,这就形成了死锁。
死锁程序其实依然在执行,只是没有任何反应。
线程的生命周期
线程有6种状态
1.新建状态:当一个线程对象被创建,但还没有调用start方法
2.可运行状态:表示当前线程已经启动,当一个线程对象,调用start方法之后,此时线程对象处于可运行状态。
可运行状态包括:就绪状态和正在运行两种状态。
就绪:表明当前线程没有被CPU所调度,但是做好了准备运行的准备,随时都可以运行。
正在运行:表明当前线程正在被CPU所调度,此时就在执行中。
3.阻塞状态:正在运行的线程遇到某个特殊情况如,同步,等待I/O操作完成等就会进入阻塞状态。
阻塞状态转换为运行状态,并不是说,马上就执行,而是处于就绪状态,等待CPU来调度它。
4.无限等待状态:
当线程种使用了wait方法,此时当前线程就进入了无限等待状态,除非,其他线程调用notify或者notifyAll方法,方可唤醒等待的线程。
5.计时等待状态:
但前线程使用了wait(long time)方法,或者使用了sleep(long time)方法。
6.终止状态/死亡状态:
线程一旦死亡,就不能再重新启动。
在Thread类中,有方法:isAlive(),用来判断线程是否处于活动状态。
计时器
public class 定时器 {
public static void main(String[] args) {
//三秒后输出hello
Date endTime = new Date(System.currentTimeMillis() + 3000);
new Timer().schedule(new TimerTask() { //这个方法的参数是TimerTask与Date对象
public void run() {//TimerTask是个抽象类,run方法需重写
System.out.println("hello");
}
}, endTime);
//每隔一秒输出一次时间
new Timer().schedule(new TimerTask(){
public void run() {
System.out.println(new Date());
}
},0,1000);//dalay是隔多长时间开始输出,period是每次输出间隔多长时间
}
}