黑马程序员 Java基础知识总结-多线程

来源:互联网 发布:砸金蛋程序源码 编辑:程序博客网 时间:2024/05/23 01:15
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。

一、进程与线程的区别:

       1.进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。

       2.线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的。

二、java中创建多线程的方式:1、继承Thread类;2、实现Runnable接口。

两种进程创建方式比较:

第二种创建多线程的方法:继承Thread类

   run()和start()方法的区别:
   创建多线程的一种方式,继承Thread类
   步骤:
    1、定义类并继承自Thread类
    2、覆写Thread类中的run()方法
    目的:将自定义的代码存储在run()方法中,让线程运行
    3、调用现成的start()方法
    目的:start()是启动线程用的

package com.blog.source;public class ThreadDemo extends Thread{public void run(){for(int x = 0 ;x <100 ; x++)System.out.println(Thread.currentThread().getName()+"在运行");}public static void main(String[] args) {new ThreadDemo().start();new ThreadDemo().start();new ThreadDemo().start();}}

继承Thread类这种实现多线程时,不能在继承其他的java类(因为java只能但集成),相同的资源也不能共享,下面看一下线程的第二种实现方式,实现Runnable接口

第二种创建多线程的方法:实现Runnable接口
   步骤:
    1、创建类并实现Runnable接口
    2、实现Runnable接口中的run方法
    3、通过Thread类建立线程对象
    4、将Runnable接口的子类作为实际参数传递给Thread的构造方法
    为什么要将Runnable接口的子类对象传递给Thread的构造方法?
    因为,自定对象所属Runnable接口的子类对象,所以要让线程去
    执行指定run方法中的代码,就必须明确该run方法所属对象。
    5、调用Thread类中的run方法,并调用Runnable接口中的run方法

package com.blog.source;public class ThreadDemo implements Runnable{public void run(){for(int x = 0 ;x <100 ; x++)System.out.println(Thread.currentThread().getName()+"在运行");}public static void main(String[] args) {ThreadDemo td = new ThreadDemo();new Thread(td).start();new Thread(td).start();new Thread(td).start();}}

实现Runnbale接口和继承Thread类有什么区别?

  实现方式,好处是避免了java中单继承的局限性,在实现多线程的时候建议
使用实现方式
  继承Thread类:线程代码存放在Thread子类run方法中
   实现Runnable:线程代码存放在接口子类的run方法中

三、线程的生命周期:

   新建状态,至今尚未启动的线程处于这种状态。
   运行状态,正在 Java 虚拟机中执行的线程处于这种状态。
   阻塞状态,受阻塞并等待某个监视器锁的线程处于这种状态。

四、控制线程:

控制线程是一件复杂的事情,java给我提供了控制线程的对象:
       join方法:调用join方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。

      sleep方法:线程休眠: 让执行的线程暂停一段时间,进入阻塞状态。

五、线程安全问题:当多条语句在操作同一数据时,当一个线程只执行了共享数据的一部分时,另外一个线程取得了执行权,修改共享数据。这种情况会导致数据修改错误。

    解决线程安全问题有三种办法,同步代码块(java中用synchronized关键字同步)、同步方法和静态方法的同步,在jdk1.5之后java引入了同步锁机制。

六、线程之间的通信:

   假设现在有一个容器,线程一和线程二都要使用这个容器,线程一使用这个容器的目的是往容器里面放东西,线程二使用这个容器里面取东西,容器里面有东西的时候线程一不能往里面放东西,容器为空的时候线程二不能从容器里面取出东西。此时只有线程一和线程二彼此通信才能知道容器里面是否有东西。

wait():让当前线程放弃监视器进入等待,直到其他线程调用同一个监视器并调用notify()或notifyAll()为止。

notify():唤醒在同一对象监听器中调用wait方法的第一个线程。

notifyAll():唤醒在同一对象监听器中调用wait方法的所有线程。

举例:生产者和消费者

生产者和消费者描述的是这样一个过程:生产者只能生产商品 ,消费者只能使用生产者生产出来的商品。此时,他们都会用到一个共同的区域,就是生产者将生产出来的产品放到这个区域,消费者将该区域内的产品取走 。在过程中会出现如果该区域内没有商品消费者则不能取,如果该区域内有商品生产者不能生产。

Resource.java描述的是生产者和消费者共同使用的资源区域 

public class Resource {/* 创建资源类 */private String reName;// 产品资源名称private int count = 0;// 交替生产,目的是为了好区分/* 如果flag为false时,生产者可以生产,此时消费者不能消费;若falg为ture则不能生产,消费者可以消费 */private boolean flag = false;public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public int getCount() {return count++;}public void setCount(int count) {this.count = count;}public String getReName() {return reName;}public void setReName(String reName) {this.reName = reName;}}

Producer.java描述生产者:

public class Producer implements Runnable {/*创建一个生产者类实现Runnable接口,创建多线程*/private Resource r = null;public Producer(Resource r) {this.r = r;}//覆写run方法实现多线程public void run() {while (true) { //一直生产synchronized (r) {/*如果flag为真则表示产品存放区有产品,此时不能生产产品,所有生产者线程应该休眠等待*/while (r.isFlag()) {try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}}if (r.getCount() == 0) {r.setReName("苹果");System.out.println(Thread.currentThread().getName()+ "--生产一个--" + r.getReName());} else {r.setReName("梨子");System.out.println(Thread.currentThread().getName()+ "--生产一个--" + r.getReName());}r.setCount(r.getCount() % 2);/*当前线程创建一个产品之后产品区有了新产品,此时不能进行生产,更改标识flag,并唤醒其他线程*/r.setFlag(true);r.notifyAll();}}}}

Consumer.java描述消费者

<pre name="code" class="java">public class Consumer implements Runnable {private Resource r = null;public Consumer(Resource r) {this.r = r;}public void run() {while (true) {synchronized (r) {/*如果flag为真,表示产品区有新产品,此时可以消费,反之则不能消费,所有消费者线程等待*/while (!r.isFlag()) {try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + "--吃掉--"+ r.getReName());/*当前线程将产品消费掉之后,更改标识,并唤醒其他线程*/r.setFlag(false);r.notifyAll();}}}}

ProducerAndConsumer.java为程序的入口 ,创建多个生产者和多个消费者

public class ProducerAndConsumer {public static void main(String[] args) { Resource r = new Resource(); //生产者线程 new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start();  //消费者线程 new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start();}}


上述代码执行的结果为:




0 0