文章目录
一、线程的概念
1.程序和进程
程序:程序是一种静态概念,磁盘上的一系列文件组成一个程序
进程:进程是一种动态概念,是运行中的程序,一个程序有一个或多个进程
2.进程和线程的区别
进程是程序执行是相关资源分配的最小单位,进程之间是相互独立互不干扰的,一个进程包含一个或多个线程
线程是CPU分配的最小单位,线程之间共享进程的内存,线程有自己独立的空间
二、串行,并行,并发
串行:多个指令一个一个的执行
并发:每个线程单独运行,CPU在这些线程中切换执行,线程并不是一起执行的
并行:多个CPU内核执行多个线程,实现多个线程同时执行
三、线程的实现
线程的实现有四种方式:
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
4.使用线程池
1. 继承Thread
1.继承Thread类,重写run方法,调用starts方法
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"启动"+i+"次");
}
}
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread1=new MyThread();
myThread1.start();
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"启动"+i+"次");
}
}
}
2.实现Runnable接口
1.实现Runnable接口,实现run方法,创建实现Runnable接口的对象,创建Thread对象传入实现Runnable接口的对象
调用start方法
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
public class RunnableTest {
public static void main(String[] args) {
//实现Runnable接口,方法一
MyRunnable myRunnable=new MyRunnable();
//实现Runnable接口,方法二,匿名内部类
Thread thread=new Thread(myRunnable);
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
});
//实现Runnable接口,方法三,lamda表达式
Thread thread2=new Thread(()->{
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
});
thread.start();
thread1.start();
thread2.start();
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
3.实现Callable接口
1.实现Callable接口,实现call方法,创建实现Callable接口的对象,创建FutrueTask对象传入实现Callable接口的对象,创建Thread对象传入FutrueTask对象,调用start方法
class MyCallable implements Callable<Long>{
@Override
public Long call() throws Exception {
long sum=0;
for (int i = 0; i < 1000000; i++) {
sum+=i;
}
return sum;
}
}
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask=new FutureTask(new MyCallable());
Thread thread=new Thread(futureTask);
thread.start();
System.out.println("执行结果");
System.out.println(futureTask.get());
}
}
Thread,Runnable,Callable创建线程的区别
1.Thread创建线程是继承,因为java是单继承,会导致继承Thread的类不能继承别的类
2.Runnable和Callable是实现接口,java可以多实现
3.在实现Runnable,和Callable接口时,必须实现run方法和Call方法,不容易出错
4.Callable有返回值,可以通过get()方法获取返回值,Runnable和Thread是没有返回值的
三、线程的生命周期
五.线程常用方法
方法 | 使用 |
---|---|
start() | 启动线程 |
stop | 停止线程,紧张使用,可能会导致线程死锁等问题 |
getName | 获得线程名称 |
setName | 设置线程名称 |
sleep | 线程休眠 |
setPriorty | 设置线程的优先级(1-10)等级越高,抢占CPU的几率更大 |
setDaemon | 设置为后台线程,TRUE,守护其他线程,为其他线程服务,如 GC(垃圾回收)如果没有其他线程,后台线程也会关闭 |
Join | 线程的加入(合并)插队 |
练习
package com.tx.work;
/**
* 1. 设计两个线程,一个线程负责打印1~100以内所有的偶数;然后,另外一个线程负责打印1~100以内所有的奇数。
*
* 测试时,分别设置线程的优先级,观察执行的顺序。
*/
public class WorkOne {
static Thread t1;
static Thread t2;
public static void main(String[] args) {
t1= new Thread(new Runnable() {
public void run() {
for (int i=1;i<=100;i++){
if (i%2==0){
System.out.println("偶数:"+i);
}
}
}
});
t2= new Thread(new Runnable() {
public void run() {
for (int i=1;i<=100;i++){
if (i%2==1){
System.out.println("奇数:"+i);
}
}
}
});
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
}
/**
* 实现一个线程,用于扫描某个目录下的所有文本文件(包括:java、txt、html),并将文字内容打印出来。
*/
public class WorkTwo {
public static void copy(String path){
File file = new File(path);
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
if (!files[i].isDirectory()) {
System.out.println();
try (InputStream inputStream = new FileInputStream(files[i].getAbsolutePath())) {
int length = 0;
byte[] bs = new byte[1024];
while ((length = inputStream.read(bs)) != -1) {
String str = new String(bs, 0, length, "utf-8");
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else {
copy(files[i].getAbsolutePath());
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
copy("D:\\work");
}
}).start();
}
}
/**
* 某人正在看电视连续剧,从第1~88集,看到第10集时,来了一个送快递的,收完快递后后,继续看电视。
*/
public class WorkThree {
static Thread thread1;
static Thread thread;
public static void main(String[] args) {
thread=new Thread(()->{
for (int i = 1; i <= 88; i++) {
System.out.println("看电视"+i);
if (i==10){
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread1=new Thread(()->{
System.out.println("送快递");
});
thread.start();
}
}
/**
* 2. 多线程模拟龟兔赛跑:
*
* 乌龟和兔子进行1000米赛跑,兔子前进5米,乌龟只能前进1米。
*
* 但兔子每20米要休息500毫秒,而乌龟是每100米休息500毫秒。
*
* 谁先到终点就结束程序,并显示获胜方
*/
public class WorkFour {
public static void main(String[] args) {
Thread rabbit=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<=1000;i+=5){
System.out.println("兔子"+i);
if (i==1000){
System.out.println("兔子胜利");
System.exit(0);
}
if (i%20==0&&i!=0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread tortoise=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 1000; i++) {
System.out.println("乌龟"+i);
if (i==1000){
System.out.println("乌龟胜利");
System.exit(0);
}
if (i%100==0&&i!=0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
rabbit.start();
tortoise.start();
}
}
总结
1.为什么用多线程
将一些占用时间长的任务放到后台去运行,来减少程序运行时间,提高程序的运行效率
2.如不执行start直接调用重写或实现的run方法会怎样?
在没有执行start,直接调用run方法,不会创建新的线程,直接在主线程中运行