一:进程与线程
1.进程(Process)
每个独立运行着的程序称为一个进程.
2.线程(Thread)
线程是一个进程内部的一条执行路径(path),Java虚拟机允许应用程序并发地运行多个执行路径
进程和线程区别:
进程有独立的地址空间,一个进程崩溃后,不会对其它进程产生影响,而线程只是一个进程中的一个执行路径,如有一条线程崩溃了,可能会影响同进程中的其他的线程。
线程有自己的栈和局部变量,多个线程共享同一进程的地址空间
一个进程至少有一个线程
4.多线程
多线程就是在一个进程中创建多个线程,每个线程完成一个任务
优点:
1) 多线程技术使程序的响应速度更快
2) 提高资源利用率
3) 程序设计更简单
多线程执行特性:
随机性(异步执行)
谁”抢”到cpu,谁执行,宏观上同时执行,微观上同一时刻只能执行一个线程
多核除外
二:线程的创建和启动
两种创建新线程的方式
第一种方式:
将类声明为Thread 的子类并重写run()方法
class MyThread extends Thread{
public void run(){
线程具体执行的代码
}
}
创建此线程类的实例并启动:
MyThread thread1 = new MyThread();
thread1.start(); // 启动线程
线程的基本使用方法:
public void start() | 使该线程开始执行 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public final String getName() | 返回该线程的名称 |
public final void setName(String name) | 改变线程名称 |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程 |
public static void sleep(long millis) | 线程休眠指定毫秒时间 |
public final void join() | 当前线程必须等待,直到调用join()方法的线程对象所对应的线程运行完毕, 当前线程才恢复执行 |
public static void yield() | 线程礼让,但操作系统可以忽略这个礼让请求 |
package thread;
public class MyThread extends Thread{
public MyThread(){
}
public MyThread(String name){
super(name);
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"正在执行,i="+i);
}
}
}
package thread;
public class ThreadDemo {
public static void main(String[] args)throws Exception {
MyThread t1=new MyThread("A线程"); // 实例化线程对象
t1.start();
MyThread t2=new MyThread("B线程");
t2.start();
try {
Thread.sleep(5000); // 当前线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("***************"+Thread.currentThread().getName()+"正在运行...*****************");
}
}
运行结果:
package thread;
public class JoinMethod {
public static void main(String[] args) {
MyThread mt = new MyThread("我的线程");
mt.start(); // 启动线程
for (int i = 0; i <= 100; i++) {
if (i == 30) {
try {
mt.join(); // 当前线程必须等待,直到调用join()方法的线程对象所对应的线程运行完毕,当前线程才恢复执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "执行,i=" + i);
}
}
}
运行结果:
package thread;
public class YieldMethod {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "执行,i=" + i);
Thread.yield(); // 给调度程序的一个提示,当前线程愿意放弃它当前的处理器的使用。调度程序可以自由地忽略这个提示。
}
}
}
运行结果:
三:线程的生命周期(状态转换)
1.线程的状态:
要想实现多线程,必须在主线程中创建新的线程对象。任何线程都具有五种状态:创建、就绪、运行、阻塞、终止。
2.线程的停止:
如果线程的run()方法中执行的是一个重复执行的循环,可以提供一个标记来控制循环是否执行。
如果线程因为执行sleep()或是wait()而进入了阻塞状态,此时要想停止它,可以使用interrupt(),程序会抛出InterruptException异常。
如果程序因为输入/输出的等待而阻塞,基本上必须等待输入/输出的动作完成才能离开阻塞状态。无法用interrupt()方法来使得线程离开run()方法,要想离开,只能通过引发一个异常。
package stop;
public class LoopThread extends Thread {
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "正在运行...");
}
}
}
package stop;
public class StopLoop {
public static void main(String[] args) {
LoopThread lt=new LoopThread();
lt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lt.setFlag(false);
System.out.println("end..........");
}
}
运行结果:
package stop;
public class InterruptThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("为什么叫醒我???!!!");
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
}
package stop;
public class StopInterrupt {
public static void main(String[] args) {
InterruptThread it=new InterruptThread();
it.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
it.interrupt(); // 打断这个线程,使之抛出InterruptedException异常
}
}
运行结果:
package copy;
import java.io.*;
/*
* 使用线程将某个目录下的文件复制到另一个目录下,在复制的过程中要显示出
完成进度(eg:已复制5%,10%...)。要求由客户端决定源文件与目标文件。
提示:要用到File类的public long length()方法。
可在复制的过程中使用Thread.sleep(xxx)来放缓复制速度;
*/
public class CopyThread extends Thread {
private File srcFile;
private File destFile;
public CopyThread() {
}
public CopyThread(File srcFile, File destFile) {
this.srcFile = srcFile;
this.destFile = destFile;
}
@Override
public void run() {
InputStream input = null;
OutputStream out = null;
try {
input = new FileInputStream(srcFile);
out = new FileOutputStream(destFile);
byte[] b = new byte[1024 * 10];
int len = 0;
int count = 0; // 用来记录已经写入的字节数
long totalSize = srcFile.length(); // 要复制的源文件的总大小,以字节计算
while ((len = input.read(b)) != -1) {
out.write(b, 0, len);
count += len;
int percentage = (int) (count * 1.0 / totalSize * 100);
System.out.println("已经下载了" + percentage + "%");
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package copy;
import java.io.File;
public class Test {
public static void main(String[] args) {
System.out.println("main方法开始了...");
File srcFile=new File("c:"+File.separator+"picture.png");
File destFile=new File("d:"+File.separator+srcFile.getName());
new CopyThread(srcFile,destFile).start(); // 启动复制线程
System.out.println("主方法结束...");
}
}
运行结果: