在学习多线程之前首先要弄清楚下面的概念。
什么是程序?
安装在磁盘上到一段指令集合,它是静态的概念什么是进程?
它是运行中的程序,是动态的概念,每个进程有独立的资源空间
什么是线程?
线程,又称为轻量级进程,是程序执行流的最小单元,是程序中一个单一的顺序控制流程。线程是进程找那个的一个实体,是被系统独立调度和分派的基本单位。
什么是多线程?
多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务。
多线程的特点
1一个进程可以包含一个或多个线程
2一个程序实现多个代码同时交替运行需要产生多个线程
3线程本身不拥有系统资源,与同属一个进程的其他程序共享所在进程所拥有的资源
4同一进程中的多个线程之间可以并发执行,CPU会随机抽取时间,让我们的程序一会儿做这件事,一会儿做另外一件事。(这是单核CPU)(多核CPU可以同时有多个线程)
多线程的目的
就是最大限度地利用CPU资源,当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其他线程有机会获得CPU资源。从根本上说,这就是 多线程编程的最终目的。
创建线程的方法有两种
第一种 通过继承Thread类创建线程
步骤1 普通Java类继承Thread类,就成为一个线程类
步骤2 重写Thread类中的run方法 run方法中的内容就是线程执行的内容
步骤3 创建一个该普通类的实例
步骤4 点start方法 启动线程
具体代码如下
package com.threadtest;
/*第一种创建线程的方法
* 直接继承Thread类
* 然后重写run方法
* run中的内容就是线程要执行的东西
*
* 线程一旦开启,程序员就不能控制了,所以每次运行的结果不一定一样
* 看cpu空闲时间
*
* */
public class HelloThreadDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
HelloThread h1=new HelloThread();
h1.setName("线程一");
h1.start();
HelloThread h2=new HelloThread();
h2.setName("线程二");
h2.start();
}
}
class HelloThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(this.getName()+":"+i);
}
}
}
创建线程的第二种方法
通过实现Runnable接口创建线程
步骤1 普通类实现Runnable接口
步骤2 实现Runnable接口中的run方法
步骤3 创建实现Runnable接口的类的实例
步骤4 创建一个Thread类对象,将上一步实例化得到的Runnable对象作为参数传入Thread类的构造方法
步骤5 通过Thread类的start方法启动线程
具体实现如下
package com.threadtest;
/*
* 创建线程的第二种方法
* 创建一个普通类,实现runnanble接口,然后重写run方法
*
* */
import javax.swing.plaf.synth.SynthStyle;
public class HelloRunnableDemo {
public static void main(String[] args) {
HelloRunnable h1=new HelloRunnable();
Thread t1=new Thread(h1,"线程名字1");
t1.start();
Thread t2=new Thread(h1,"线程名字2");
t2.start();
}
}
class HelloRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
//这里的Thread.currentThread().getName()是获取当前线程的名字
//与第一种方法不同,这里的类只是普通类,没有继承Thread类,所以没有getName方法,
//就不能直接调用,这里先获取当前线程的对象,返回的是一个Thread对象,在调用线程的方法就可以了。
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
实际开发中创建线程多使用第二种方法,即实现Runnable接口的方法来创建线程。
实现Runnable接口来创建线程 相比继承Thread类来创建线程的优点如下:
* 1 避免实现单继承的局限,一个类可以实现多个接口
* 2 适合资源共享
关于资源共享,比如有两个售票窗口,只有五张票,如果要使用继承Thread方法来创建线程,就要实现两个线程实例,每个实例就会有五张票,各卖各的,不能实现资源共享。具体代码解释如下:
package com.threadtest;
/** 实际开发中创建线程多使用第二种方法,即实现Runnable方法。
* 实现Runnable接口来创建线程 相比继承Thread类来创建线程的优点
* 1 避免实现单继承的局限,一个类可以实现多个接口
* 2 适合资源共享
* */
public class ShareDataThreadDemo {
public static void main(String[] args) {
/*两个窗口买五张票,但是用这种方法,两个窗口都是new出来的对象。
每个对象都有五张票,各卖各的。
*/
TicketThread s1=new TicketThread("一号窗口");
s1.start();
TicketThread s2=new TicketThread("二号窗口");
s2.start();
}
}
class TicketThread extends Thread{
private int ticket=5;
public TicketThread(String name) {
super(name);
}
@Override
public void run() {
while(true){
System.out.println(this.getName()+":"+(ticket--));
if(ticket<1){
System.out.println("没票了");
break;
}
}
}
}
如果用实现Runnable接口来创建线程,可以实现资源共享。
具体的代码如下
package com.threadtest; /** 实际开发中创建线程多使用第二种方法,即实现Runnable方法。 * 实现Runnable接口来创建线程 相比继承Thread类来创建线程的优点 * 1 避免实现单继承的局限,一个类可以实现多个接口 * 2 适合资源共享 * */ public class ShareDataThreadDemo { public static void main(String[] args) { //实现Runnable接口来创建线程。来达到资源共享 TicketRunnable tr=new TicketRunnable(); //这里五张票只属于tr这个对象。下面两个线程操作的都是这五张票 //这里就实现了数据的共享 Thread t1=new Thread(tr,"一号窗口"); t1.start(); Thread t2=new Thread(tr,"二号窗口"); t2.start(); } } class TicketRunnable implements Runnable{ private int ticket=5; @Override public void run() { while(true){ System.out.println(Thread.currentThread().getName()+":"+(ticket--)); if(ticket<1){ System.out.println("票卖完了"); break; } } } }