概念
程序:指令和数据的有序集合,本身没有运行的含义,是一个静态的概念
进程:程序的一次执行过程,是一个动态的概念,有自己的生命周期,是系统资源分配的基本单位
线程:是CPU调度和执行的基本单位
- 一个程序运行时至少有一个进程,一个进程至少有一个线程
- 程序运行时,即使自己没有创建线程,在java中也会有两个线程,即主线程和守护线程
- main()称为主线程,是系统执行的入口
- 当有多个线程时,线程的运行由调度器安排调度。调度器与操作系统密切相关,先后顺序不能人为干预。线程具有优先级,但是最终调用谁,还得看CPU
- 系统会为进程分配资源,进程中的多个线程会共享这个资源。因此多线程会存在抢夺资源的问题,需要加入并发控制
- 线程会带来额外开销,如CPU调度、并发控制开销
- 很多多线程都是模拟出来的,即在一个CPU的情况下,同一个时间点,CPU只能执行一个线程,但是切换速度很快,所以会以为是多个线程同时执行。真正的多线程是指有多个CPU。即多核。
普通方法调用和多线程
三种创建线程的方式
Thread类
Thread实现了Runnable接口
步骤:1、自定义线程类继承Thread类;2、重写run()方法,编写线程执行代码;3、创建自定义线程对象,调用start()方法启动线程
//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
public class TestThread01 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 2000; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
//main主线程
//创建一个线程对象
TestThread01 testThread01 = new TestThread01();
//线程启动
testThread01.start();
for (int i = 0; i < 2000; i++) {
System.out.println("我在学习---"+i);
}
}
}
Runnable接口
推荐使用这个方法,1、因为java单继承的局限性,最好是实现接口而不是继承;2、静态代理思想,可以一份资源,多个代理(后面章节详细讲)
步骤:1、自定义类实现Runnable接口;2、实现run()方法,编写线程执行体;3、创建自定义类的对象,并将其作为Thread对象的参数 ;4、start()
下面的例子自己开辟了三个线程,只需要一个自定义类对象,三个Thread对象。还可以给线程起名字new Thread(test,"A")
以及获得当前正在执行的线程名字Thread.currentThread().getName()
。
public class TestThread01 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ": "+ "我在看代码---"+i);
}
}
public static void main(String[] args) {
//main主线程
//启动三个线程,每个线程各自都去执行run方法,都输出2000句
TestThread01 test = new TestThread01();
new Thread(test,"A").start();
new Thread(test,"B").start();
new Thread(test,"C").start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习---"+i);
}
}
}
Callable接口(目前了解)
实现Callable接口,重写call方法,有返回值,需要指定返回值类型(下面代码中指定为Boolean类型)
步骤:1、创建目标对象;2、创建服务ExecutorService service = Executors.newFixedThreadPool(n)
;3、提交执行Future<Boolean> submit1 = service.submit(t1)
;4、获取结果submit1.get()
;5、关闭服务service.shutdownNow()
举例:开启三个线程测试
public class TestCallable01 implements Callable<Boolean>{
@Override
public Boolean call() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "我在看代码---"+i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable01 t1 = new TestCallable01();
TestCallable01 t2 = new TestCallable01();
TestCallable01 t3 = new TestCallable01();
//创建服务,指定线程池中的线程数
ExecutorService service = Executors.newFixedThreadPool(3);
//提交线程
Future<Boolean> submit1 = service.submit(t1);
Future<Boolean> submit2 = service.submit(t2);
Future<Boolean> submit3 = service.submit(t3);
//获取结果
submit1.get();
submit2.get();
submit3.get();
//关闭服务
service.shutdownNow();
}
}
测试:多线程下载图片
需要先导入一个commons-io-2.6.jar
包,用FileUtils.copyURLToFile(new URL(url), new File(name))
方法从网络上下载资源,传入URL和存储地址
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//继承Thread
public class TestThread02 extends Thread{
private String url;
private String name;
@Override
public void run() {
try {
new WebDowner().down(url,name);
} catch (Exception e) {
e.printStackTrace();
System.out.println(name+"下载异常");
}
}
public TestThread02(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) {
TestThread02 t1 = new TestThread02("https://img0.baidu.com/it/u=2151136234,3513236673&fm=26&fmt=auto&gp=0.jpg", "C:\\Users\\Bi\\Desktop\\1.jpg");
TestThread02 t2 = new TestThread02("https://c-ssl.duitang.com/uploads/item/201503/21/20150321160159_vVxSC.thumb.1000_0.jpeg","2.jpg");
TestThread02 t3 = new TestThread02("https://c-ssl.duitang.com/uploads/item/201503/12/20150312231447_THNsw.thumb.1000_0.jpeg","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
class WebDowner{
//下载方法
public void down(String url, String name) throws Exception {
FileUtils.copyURLToFile(new URL(url), new File(name));
System.out.println(name+"下载完毕");
}
}