java如何实现多线程


       首先,java创建线程的方式有两种,一个是继承Thread类,一个是实现Runnable接口,这两种方式线程执行完后没有返回值。

第一种,继承Thread类:

写一个Thread类的子类MyThread

 

public class MyThread extends Thread {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
} 


MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start(); 


         其实,Thread类也是实现了Runnable接口的,启动线程时调用Thread类的start()方法,此时它会执行run()方法,也就是我们在子类MyThread中重写的run()方法。这种继承的方式虽然简单,但是因为java不支持多继承,因此当一个类已经继承了一个父类时,无法再用这种方式实现多线程,所以还有另外一种更灵活的方式。


第二种,实现Runnable接口:

 

public class MyThread extends FatherClass implements Runnable {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
} 


MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start();  

 

        我们看到,Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,通过Thread类的构造函数来实现将MyThread的实例传给Thread实例。调用thread的start()方法启动线程,此时thread会执行自身的run()方法,由于如下代码,最终调用的是我们在MyThread中实现的run()方法。

 

 

public void run() {  
  if (target != null) {  
   target.run();  
  }  
}  

         其次,线程的主要方法sleep(),join(),yield(),wait(),notify()?前三个方法是Thread类中的方法,后两个是Object类中的方法。它们对线程有什么样的影响呢?

         线程的五种状态如图所示:

1.sleep方法:

让当前线程暂停指定的时间,线程睡眠时进入阻塞状态,到期自动苏醒,并返回到就绪状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

2.yield方法:

线程是存在优先级的,设置范围在1~10之间,默认是5。JVM线程调度程序是基于优先级的抢先调度机制。当线程池中的线程具有相同的优先级时,调度程序的JVM有两种可能的调度方式:要么选择一个线程运行直到它运行完成或阻塞,要么时间分片,使线程池内的每个线程有均等的运行机会。所以,依赖于优先级往往不是那么靠谱的。

yield方法会暂停当前正在运行的线程,该线程进入就绪状态,并执行其他线程,是一种让步行为。yield为其他具有相同优先级的线程提供了获得运行的机会,使得这些线程能适当的轮询,但不排除让步的线程被再次选中运行。

3.join方法:

暂停当前线程,直到join的线程运行完成,再继续执行。作用在于将异步执行的线程合并为同步(顺序)执行的线程。在join的源码实现中,使用了wait方法来将线程阻塞。

4.wait方法:

wait方法的使用必须在同步的范围内,否则就会抛出IllegalMonitorStateException异常,wait方法的作用就是阻塞当前线程等待notify/notifyAll方法的唤醒,或等待超时后自动唤醒。wait方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的,java中只能通过Synchronized关键字获取到monitor对象的所有权。

5.notify/notify All方法:

同样的,notify方法的使用也必须在同步的范围内,wait是通过对象的monitor对象来实现的,所以只要在同一对象上调用notify/notifyAll方法,就可以唤醒对应对象monitor上等待的线程。notify和notifyAll的区别在于前者只能唤醒monitor上的一个线程,对其他线程没有影响,而notifyAll则唤醒所有的线程。

有一个典型的生产者与消费者问题,可以帮助我们深入理解并发编程中的线程协作。

/**
 * 产品:苹果
 */
public class Apple {
    int id;

    Apple(int id){
        this.id = id;
    }

    public String toString(){
        return "Apple Id : " + id;
    }
}


/**
 * 生产者
 */
public class Producer implements Runnable {
    SyncStack ss = null;
    Producer(SyncStack ss){
        this.ss=ss;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            Apple apple = new Apple(i);
            ss.push(apple);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


/**
 * 消费者
 */
public class Consumer implements Runnable {

    SyncStack ss = null;

    Consumer (SyncStack ss){
        this.ss = ss;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            Apple apple = ss.pop();
            try {
                Thread.sleep((int)(Math.random()*1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



/**
 * 盛放苹果的栈容器
 */
public class SyncStack {

    int index = 0;//个数
    Apple[] arrApple = new Apple[6];

    public synchronized void push(Apple apple){
        while(index == arrApple.length ){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();
        arrApple[index] = apple;
        System.out.println("生产了" + apple);
        index++;
    }

    public synchronized Apple pop(){
        while (index == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();
        index--;
        System.out.println("消费了:" + arrApple[index]);
        return arrApple[index];
    }

    public static void main(String[] args){
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);
        new Thread(p).start();
        new Thread(c).start();
    }
}

解释一下这个经典问题:

容器中最多盛放6个苹果,生产者的线程每生产一个苹果时,会notify等待容器对象锁的线程,告诉它(们)容器中有苹果了,可以拿。至于消费者的线程什么时候拿这个苹果,得看JVM调度以后,也就是消费者线程获得对象锁,被CPU运行的时候。如果生产者生产的速度比消费者消费的速度快,也就是会出现容器放满苹果的现象,此时,生产者无法再继续生产苹果,发生阻塞事件,容器对象调用wait方法,生产者释放对容器对象的锁,进入阻塞状态,并等待被notify。消费者的线程开始执行,消费一个苹果,解除生产者的阻塞条件,此时调用notify方法,唤醒(所有)等待容器对象锁的线程,告诉它(们)容器中有空位了,可以放。等到消费者线程退出执行,生产者线程即可运行,如此协作。

 

 

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值