Java中对多线程的认识

目标

了解进程与线程的区别。

掌握Java线程的两种实现方式及其区别。

了解线程的操作状态。

进程与线程

DOS系统有一个非常明显的特点,只有一中病毒之后系统会立刻死机,因为传统的DOS是采用单进程的处理方式,所以只能有一个程序运行,其他程序无法运行。

Windows系统中,即使出现了病毒,系统照样可以使用,因为在windows中采用的是多进程的处理方式,那么在同一个时间段上会有多个程序同时运行。

线程实际上就是在进程的基础之上进一步划分,从WORD来看,可以吧拼写检查当做一个进程进行处理。当然会存在多个线程。如果一个进程没有了,则线程肯定会消失,那么如果线程消失了,但进程未必会消失。而且所有的线程都是在进程的基础之上并发(同时运行)。

如果现在同时运行多个任务,则所有的系统资源将是共享的,被所有线程所公用,但是程序处理需要CPU,传统的单核CPU来说,在同一时间段上会有多个程序执行,但是在同一个时间点上只能存在一个程序运行,也就是说所有程序都要抢占CPU资源。

但是现在的CPU已经发展到多核的状态了,在一个电脑上可能会存在多个CPU,那么这个时候就可以非常清楚的发现多线程操作间是如何并发执行的。

java的多线程实现

在java中如果想实现多线程可以采用以下两种方式:

继承Thread类。

实现Runnable接口。

Thread类

继承Thread类

Thread类是在java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程的操作类,在Thread子类之中,必须明确的覆写Thread类中的run()方法,此方法为线程的主体。

java.lang包会在程序运行时自动导入,所以无需手工编写import语句。

一个类继承了Thread类之后,那么此类就具备了多线程的操作功能。

class MyThread extends Thread{	// 继承Thread类,作为线程的实现类
	private String name ;		// 表示线程的名称
	public MyThread(String name){
		this.name = name ;		// 通过构造方法配置name属性
	}
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<10;i++){
			System.out.println(name + "运行,i = " + i) ;
		}
	}
};
public class ThreadDemo01{
	public static void main(String args[]){
		MyThread mt1 = new MyThread("线程A ") ;	 // 实例化对象
		MyThread mt2 = new MyThread("线程B ") ;	 // 实例化对象
		mt1.run() ;	// 调用线程主体
		mt2.run() ;	// 调用线程主体
	}
};

从此处的运行结果看,并未出现抢夺资源的现象。

以上程序是先执行完A之后再执行B,并未达到所谓并发执行的效果。

因为以上程序实际上还是按照古老的形式调用的,通过对象.方法,但是如果想启动一个线程必须使用Thread类中定义的start()方法。

一旦调用start()方法,实际上最终调用的就是run()方法。代码如下:

class MyThread extends Thread{	// 继承Thread类,作为线程的实现类
	private String name ;		// 表示线程的名称
	public MyThread(String name){
		this.name = name ;		// 通过构造方法配置name属性
	}
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<10;i++){
			System.out.println(name + "运行,i = " + i) ;
		}
	}
};
public class ThreadDemo02{
	public static void main(String args[]){
		MyThread mt1 = new MyThread("线程A ") ;	 // 实例化对象
		MyThread mt2 = new MyThread("线程B ") ;	 // 实例化对象
		mt1.start() ;	// 调用线程主体
		mt2.start() ;	// 调用线程主体
	}
};

观察调用start()方法后确实起到了程序并发运行的效果,哪个线程抢夺到了CPU资源,哪个线程就先运行。

问题1:

为什么不直接调用run()方法,而是通过start()调用呢?

如果要想解决这样的问题,查看java.lang.Thread类,发现定义如下:

 public synchronized void start() {
        /**
	 * This method is not invoked for the main method thread or "system"
	 * group threads created/set up by the VM. Any new functionality added
	 * to this method in the future may have to also be added to the VM.
	 *
	 * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
	    stop0(throwableFromStop);
	}
    }

private native void start0();

start()方法有可能抛出异常。

stopBeforeStart是一个boolean类型的变量。

native 关键字表示的是一个由java调用本机操作系统函数的一个关键字。在java中,运行Java程序调用本机的操作系统的函数以完成特定的功能。

证明:如果现在要是想实现多线程的话,则肯定需要操作系统的支持,因为多线程操作中牵扯到一个抢占CPU的情况,要等待CPU进行调度,那么这一点肯定需要操作系统的底层支持,所以使用了native调用本机的系统函数,而且在各个操作系统中多线程的实现底层代码肯定是不同的,所以使用native关键字也可以让JVM自动调整不同的JVM的实现。

threadStatus也表示一种状态,如果线程已经启动了,再调用start()方法就有可能产生异常。

class MyThread extends Thread{	// 继承Thread类,作为线程的实现类
	private String name ;		// 表示线程的名称
	public MyThread(String name){
		this.name = name ;		// 通过构造方法配置name属性
	}
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<10;i++){
			System.out.println(name + "运行,i = " + i) ;
		}
	}
};
public class ThreadDemo03{
	public static void main(String args[]){
		MyThread mt1 = new MyThread("线程A ") ;	 // 实例化对象
		mt1.start() ;	// 调用线程主体
		mt1.start() ;	// 错误
	}
};

Runnable接口

实现Runnable接口:在Java中也可以通过实现Runnable接口的方式实现多线程,Runnable接口中只定义了一个抽象方法。

public void run();

通过Runnable接口实现多线程:

class MyThread implements Runnable{	// 实现Runnable接口,作为线程的实现类
	private String name ;		// 表示线程的名称
	public MyThread(String name){
		this.name = name ;		// 通过构造方法配置name属性
	}
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<10;i++){
			System.out.println(name + "运行,i = " + i) ;
		}
	}
};

如果想启动线程则肯定依靠Thread类,但是之前如果直接继承了Thread类,则可以将start()方法直接继承下来并使用,但是在Runnable接口中并没有此方法。

Thread类的构造:

public Thread(Runnable target);

就利用以上的构造方法启动多线程。

class MyThread implements Runnable{	// 实现Runnable接口,作为线程的实现类
	private String name ;		// 表示线程的名称
	public MyThread(String name){
		this.name = name ;		// 通过构造方法配置name属性
	}
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<10;i++){
			System.out.println(name + "运行,i = " + i) ;
		}
	}
};
public class RunnableDemo01{
	public static void main(String args[]){
		MyThread mt1 = new MyThread("线程A ") ;	 // 实例化对象
		MyThread mt2 = new MyThread("线程B ") ;	 // 实例化对象
		Thread t1 = new Thread(mt1) ;		// 实例化Thread类对象
		Thread t2 = new Thread(mt2) ;		// 实例化Thread类对象
		t1.start() ;	// 启动多线程
		t2.start() ;	// 启动多线程
	}
};

从程序运行效果可以发现,已经完成了多线程的功能。

Thread类与Runnable接口

Thread类的定义:public class Thread extends Object implements Runnable

从定义的格式上可以发现,Thread类也是Runnable接口的子类。

从类的关系上来看,之前的做法非常类似于代理设计模式,Thread类完成比线程主体更多的操作,例如:分配CPU资源,判断是否已经启动等等。

Thread类与Runnable接口的区别

使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享。

class MyThread extends Thread{	// 继承Thread类,作为线程的实现类
	private int ticket = 5 ;		// 表示一共有5张票
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<100;i++){
			if(this.ticket>0){
				System.out.println("卖票:ticket = " + ticket--) ;
			}
		}
	}
};
public class ThreadDemo04{
	public static void main(String args[]){
		MyThread mt1 = new MyThread() ;	 // 实例化对象
		MyThread mt2 = new MyThread() ;	 // 实例化对象
		MyThread mt3 = new MyThread() ;	 // 实例化对象
		mt1.run() ;	// 调用线程主体
		mt2.run() ;	// 调用线程主体
		mt3.run() ;	// 调用线程主体
	}
};

发现一共卖出了15张票。证明:三个线程各自卖各自的票,也就是说并没有达到资源共享的目的。

因为在每一个MyThread对象中都包含各自的ticket属性。

如果现在使用Runnable接口呢?同样启动多个线程,那么所有的线程将卖出共同的5张票。

class MyThread implements Runnable{	// 继承Thread类,作为线程的实现类
	private int ticket = 5 ;		// 表示一共有5张票
	public void run(){	// 覆写run()方法,作为线程 的操作主体
		for(int i=0;i<100;i++){
			if(this.ticket>0){
				System.out.println("卖票:ticket = " + ticket--) ;
			}
		}
	}
};
public class RunnableDemo02{
	public static void main(String args[]){
		MyThread mt = new MyThread() ;	 // 实例化对象
		new Thread(mt).run() ;	// 调用线程主体
		new Thread(mt).run() ;	// 调用线程主体
		new Thread(mt).run() ;	// 调用线程主体
	}
};

从运行的结果看,虽然现在启动了三个线程,但是三个线程一共才卖出了5张票,达到了资源共享的目的。

Thread类与Runnable接口的使用结论

实现Runnable接口比继承Thread类有如下的明显优点:

适合多个相同程序代码的线程去处理同一个资源。

可以避免由于单继承局限所带来的影响。

增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。

综合以上来看,开发中使用Runnable接口是最适合的。

线程的状态:

多线程在操作中也是有一个固定的操作状态的:

创建状态:准备好了一个多线程对象,Thread t = new Thread()

就绪状态:调用了start()方法,等待CPU进行调度。

运行状态:执行run()方法。

阻塞状态:暂时停止执行,可能将资源交给其他线程使用。

终止状态(死亡状态):线程执行完毕了,不再进行的使用了。

进程挂起、阻塞和睡眠的区别?

阻塞是进程在等待某种资源,但是不能马上得到,必须等待别的进程释放资源才能继续,属于被动无法得到时间片,内核就切换其他进程运行。休眠一般为主动的放弃一段CPU时间。挂起是运行时间片到了,内核要调度其他进程运行,被动式的失去CPU。

时间: 2024-08-07 21:21:00

Java中对多线程的认识的相关文章

Java中的多线程你只要看这一篇就够了

Java中的多线程你只要看这一篇就够了 引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其实只有一半对,因为反应"多角色"的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的"生产者,消费者模型". 很多人都对其中的一些概念不够明确,如同步.并发等等,让我

为什么Java中实现多线程的方式有两种?

在面试的过程中,我们经常问被面试者,为什么Java中实现多线程的方式有两种(一种是直接继承Thread类,一种是实现Runnable接口)?可惜的是,很多面试者都答不出来,甚至从来没有想为什么.,那么真正的原因是什么呢?我们可以用反证法推理一下: 假设Java只提供Thread供大家继承从而实现多线程,考虑下面的一个需求,如果有一个已经继承了某个父类的类,但是这个类又想实现多线程,怎么办?很显然,如果只提供一个可以继承的类,肯定解决不了这个问题.那么,如何解决,毫无疑问,就只能使用接口了.

Java中使用多线程、curl及代理IP模拟post提交和get访问

Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! [java] view plaincopyprint? /** * @组件名:javaDemo * @包名:javaDemo * @文件名:Jenny.java * @创建时间: 2014年8月1日 下午5:53:48 * @版权信息:Copyright ? 2014 eelly Co.Ltd,小姨子版权所有. */ package javaDemo; impo

Java中的 多线程编程

Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂.在多线程访问共享数据的时候,这部分代码需要特别的注意.线程之间的交互往往非常复杂.不正确的线程同步产生的错误非常难以被发现,并且重现以修复. 2)上下文切换的开销当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另

Java中的多线程=你只要看这一篇就够了

如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”. 很多人都对其中的一些概念不够明确,如同步.并发等等,让我们先建立一个数据字典,以免产生误会. 多线程:指的是这个程序(一个进程)运

Java中使用多线程、curl及代理IP模拟post提交和get訪问

Java中使用多线程.curl及代理IP模拟post提交和get訪问 菜鸟,多线程好玩就写着玩.大神能够路过不吝赐教.小弟在这受教.谢谢! 很多其它分享请关注微信公众号:lvxing1788 ~~~~~~ 切割线扭起来 ~~~~~~ /** * @组件名:javaDemo * @包名:javaDemo * @文件名称:Jenny.java * @创建时间: 2014年8月1日 下午5:53:48 * @版权信息:Copyright ? 2014 eelly Co.Ltd,小姨子版权全部. */

黑马程序员【Java中的多线程】

Java中的多线程 首先,在开篇讲线程之前要说一个问题,我们知道多线程的执行原理是cpu在不同的线程中做着切换操作,而一提到多线程,大家首先想到的肯定是提高系统的运行效率,可是真的是这样的么?我们来借助一位大神博客中的代码就可以看出来有时单线程的运行效率要高于多线程: import threading from time import ctime class MyThread(threading.Thread): def __init__(self, func, args, name): thr

java中实现多线程的两种基本方法

java中实现多线程有两种基本方法,一种是继承Thread, 另一种是实现Runnable接口. 但是因为java中子类只能继承一个父类,如果采用继承Thread类,就不能继承其他类,很受限制. 以下是采用继承Thread类的例子: public class MyThreadTest{ public static void main(String[] args){ MyThread amythread1=new MyThread("my thread 1"); MyThread amy

java 中的多线程

java中实现多线程的方式有两种: 1.实现继承Thread 类的类(重写run方法) 2.实现Runnable 接口(重写run方法) 上述两种方式的关系; 看过jdk你会发现 Thread类是实现了 runnable的接口了的   可见,实现Runnable接口相对于继承Thread类来说,有如下显著的优势: (1). 适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码.数据有效分离,较好地体现了面向对象的设计思想. (2). 可以避免由于Java的单继承特性带

Java中的多线程技术全面详解

本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上,"多进程"并不是真正的多个进程在同时执行,而是通过CPU时间分片,操作系统快速在进程间切换而模拟出来的多进程.我们通常把这种情况成为并发,也就是多个进程的运行行为是"一并发生"的,但不是同时执行的,因为CPU核数的限制(PC和通用寄存器只有一套,严格来说在同一时刻只能