Java多线程(1)
Java多线程
- 本系列博客为学习Java多线程(狂神说)时所做的笔记
- 本篇博客内容为多线程的三种创建方式
进程和线程
进程(Process):进程是资源分配的基本单位,它是程序执行时的一个实例,在程序运行时创建。
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列。进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
线程(Thread):线程是程序执行的最小单位,是进程的一个执行流,一个线程由多个线程组成的。
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位。一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
—摘自php中文网
程序和进程
由此可知,线程和进程之间有一种包含和被包含的关系,我们可以将进程和程序做一个比较。程序是指令和数据的有序集合,是一个静态的概念,而进程是动态的。
而多线程则是多个线路同时运行,很多多线程都是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,在一个cpu下,同一个时间片内只能执行一个代码,由于时间片很短,切换的很快,看起来想同时执行,有一个宏观和微观的区别。
线程的创建
三种创建方式
继承Thread类(重点)Thread类也继承了Runnable接口
1. 自定义线程类继承Thread类
2. 重现run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程
//创建线程的方式一
public class testThread extends Thread{
//run方法线程体
@Override
public void run() {
for (int i =0;i<20;i++){
System.out.println("我在看代码---"+i);
}
}
//main线程
public static void main(String[] args) {
//创建一个线程
testThread thread1 = new testThread();
//调用线程的start方法
thread1.start();
/*
这里我们需要注意的是,run()和start()是不一样的。
run()方法它是只有主线程再运行,按顺序运行到run()
时,把run运行完才继续,可以用嵌入式里的中断来理解。
而start()是不一样的,start时多线程运行的,具体哪
个时间片运行哪个线程纯看CPU心情。
*/
//thread1.run();
for (int i =0;i<20;i++){
System.out.println("我在学习---"+i);
}
}
}
小重点:线程开启不一定立即执行,具体有CPU调度。start()方法其实是开启了一个线程,然后这个线程里自行调用run()方法,小细节,嘿嘿嘿。
//多线程同步下载图片
public class TestThread extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名字
public TestThread(String url,String name){
this.url = url;
this.name = name;
}
//线程执行体
@Override
public void run(){
WebDownloader webDownloader = new WebDownloader();
webDownloader.download(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread thread1 = new TestThread("https://img.php.cn/upload/article/000/000/028/5ccfc59f97a82532.jpg","thread1.jpg");
TestThread thread2 = new TestThread("https://img.php.cn/upload/article/000/000/028/5ccfc5a49e323744.jpg","thread2.jpg");
TestThread thread3 = new TestThread("https://img.php.cn/upload/article/000/000/028/5ccfc5bfa2c62416.jpg","thread3.jpg");
//这个start启动不应该是按照顺序执行的吗?是由于启动时间比较久吗
thread1.start();
thread2.start();
thread3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void download(String url,String name){
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e){
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
这是一个小测试,但是这里有一个问题,执行顺序很显然每次运行都是不一样的。但是这三个线程的start方法不应该是按顺序进行的吗?理论上不应该越靠前越有可能先执行吗?(博主还没有得出答案,欢迎大家指教!)
实现Runnable接口(核心重点)
- 定义Runnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
推荐使用Runnable对象,因为Java单继承的局限性
public class TestThread3 implements Runnable{
//run方法线程体
@Override
public void run() {
for (int i =0;i<20;i++){
System.out.println("我在看代码---"+i);
}
}
//main线程
public static void main(String[] args) {
//创建一个接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建一个线程对象,通过线程对象来开启
Thread thread = new Thread(testThread3);
thread.start();
//new Thread(testThread3).start();
for (int i =0;i<20;i++){
System.out.println("我在学习---"+i);
}
}
}
其实Runnable接口也是利用了thread.start(),不过在创建thread的时候需要把实现了Runnable接口的对象作为参数传入其中。
第一种和第二种的部分区别
第一种不建议使用,因为Java不可以多继承,一个对象无法被多个线程使用。
第二章避免了单继承局限性,方便一个对象被多个线程使用。
演示多个线程操作同一个对象
//多个线程操作同一个资源的时候,线程不安全,数据紊乱
//多个线程同时操作一个对象
public class TestThread4 implements Runnable{
private int ticketNums = 10;
@Override
public void run() {
while(true){
if (ticketNums <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
new Thread(testThread4,"小明").start();
new Thread(testThread4,"老师").start();
new Thread(testThread4,"黄牛").start();
}
}
根据狂神说的课程,跟着敲了个龟兔赛跑:
//模拟龟兔赛跑
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
//这里i=1吧,按照课程里为0的话,这兔子直接被扼杀在了起跑线,太惨了
for (int i =1;i<=100;i++){
if (Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean flag = gameOver(i);
if (flag == true){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否结束
private boolean gameOver(int steps){
if (winner != null){
return true;
} else if (steps == 100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
实现Callable接口(了解)
流程:
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行文件:ExecutorService ser = Executors.newFixThreadPool(1);
- 提交执行:Future
result1 = ser.submit(1); - 获取结果:boolean r1 = result1.get();
- 关闭服务: ser.shutdownNow();
public class Testdemo5 implements Callable<Boolean> {
private String url;//网络图片地址
private String name;//保存的文件名字
public Testdemo5(String url,String name){
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.download(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Testdemo5 thread1 = new Testdemo5("https://img.php.cn/upload/article/000/000/028/5ccfc59f97a82532.jpg","thread1.jpg");
Testdemo5 thread2 = new Testdemo5("https://img.php.cn/upload/article/000/000/028/5ccfc5a49e323744.jpg","thread2.jpg");
Testdemo5 thread3 = new Testdemo5("https://img.php.cn/upload/article/000/000/028/5ccfc5bfa2c62416.jpg","thread3.jpg");
//1. 创建执行文件:ExecutorService ser = Executors.newFixedThreadPool(1);//创建线程池
ExecutorService ser = Executors.newFixedThreadPool(3);
//2. 提交执行:Future<Boolean> result1 = ser.submit(t1);//提交执行
Future<Boolean> result1 = ser.submit(thread1);
Future<Boolean> result2 = ser.submit(thread2);
Future<Boolean> result3 = ser.submit(thread3);
//3. 获取结果:boolean r1 = result1.get();
boolean rs1 = result1.get();
boolean rs2 = result2.get();
boolean rs3 = result3.get();
System.out.println(rs1 +"|"+ rs2 +"|"+ rs3);
//4. 关闭服务: ser.shutdownNow();
ser.shutdownNow();
}
}
好处:
-
可以检查返回值
-
可以抛出异常

浙公网安备 33010602011771号