多线程简介:
在学习多线程之前,首先了解一下什么是多线程?
多任务
多任务比如说一个人他可以边吃饭,边玩手机,看似一起发生的行为,但其实是上一秒在吃饭,下一秒在玩手机,本质上同一时间只做一件事情。
进程Process
进程就是程序执行的过程,他是一个动态的概念,一个进程中可以包含若干个线程,至少要有一个线程,否则毫无意义。他是操作系统进行资源分配的最小单位,每启动一个进程,操作系统就会为它分配一个独立的内存空间。
线程Thread
线程是CPU调度和执行的单位,一个线程就是一个任务执行的一次执行过程,线程不占内存空间,他是包含在进程内存空间里面的。因为一个CPU同一时间只能做一件事情,所以真正的多线程是指有多个CPU,即多核,如服务器。在程序运行时,即使没有创建线程,后台也会有多个线程,如主线程(用户线程),gc线程(守护线程)。
线程创建
方式一:继承Thread类
步骤:继承Thread类,重写run()方法,在主线程(main()函数)里创建该类对象,通过该对象调用start()方法开启线程。
示例:模拟多线程执行任务
package com. xct. test ;
public class Test1 extends Thread {
@Override
public void run ( ) {
for ( int i = 0 ; i < 200 ; i++ ) {
System . out. println ( "我在敲代码" + i) ;
}
}
public static void main ( String [ ] args) {
Test1 test1 = new Test1 ( ) ;
test1. start ( ) ;
for ( int i = 0 ; i < 800 ; i++ ) {
System . out. println ( "我在学习" + i) ;
}
}
}
注意:线程开启不一定立即执行,由CPU调度执行。
方式二:实现Runnable接口
步骤:实现Runnable接口=》重写run()方法,在主线程里创建该类对象,再创建线程Thread对象,通过线程对象调用start()方法开启线程(代理)。
示例:实现多线程下载图片(准备工作:百度下载commons-io.2.6.jar包)
package com. xct. test ;
import org. apache. commons. io. FileUtils ;
import java. io. File ;
import java. io. IOException ;
import java. net. URL;
public class Test2 extends Thread {
private String url;
private String name;
public Test2 ( String url, String name) {
this . url = url;
this . name = name;
}
@Override
public void run ( ) {
WebDownLoader webDownLoader = new WebDownLoader ( ) ;
webDownLoader. downloader ( url, name) ;
System . out. println ( "下载了图片,名为" + name) ;
}
public static void main ( String [ ] args) {
Test2 t1 = new Test2 ( "https://img0.baidu.com/it/u=1472391233,99561733&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666544400&t=d5daee453462255eb4d0fe7c29511d87" , "1.jpg" ) ;
Test2 t2 = new Test2 ( "https://img0.baidu.com/it/u=1472391233,99561733&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666544400&t=d5daee453462255eb4d0fe7c29511d87" , "2.jpg" ) ;
Test2 t3 = new Test2 ( "https://img0.baidu.com/it/u=1472391233,99561733&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666544400&t=d5daee453462255eb4d0fe7c29511d87" , "3.jpg" ) ;
t1. start ( ) ;
t2. start ( ) ;
t3. start ( ) ;
}
}
class WebDownLoader {
public void downloader ( String url, String name) {
try {
FileUtils . copyURLToFile ( new URL ( url) , new File ( name) ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
System . out. println ( "IO异常,图片下载错误" ) ;
}
}
}
小结
继承Thread类
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
实现Runnable接口
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象呗多个线程使用。
方式三:实现Callable接口
步骤:实现Callable接口=》重写call<>方法,在主线程中创建实现类对象,创建执行服务,提交执行,获取结果,关闭服务。
好处:可以定义返回值,可以抛出异常,该方式了解即可。
示例:实现多线程执行任务
package com. xct. test ;
import java. util. concurrent. * ;
public class Test6 implements Callable < Boolean > {
@Override
public Boolean call ( ) {
for ( int i = 0 ; i < 500 ; i++ ) {
System . out. println ( "我在学习" + i) ;
}
return true ;
}
public static void main ( String [ ] args) throws ExecutionException , InterruptedException {
for ( int i = 0 ; i < 600 ; i++ ) {
System . out. println ( "我在打游戏" + i) ;
}
Test6 t1 = new Test6 ( ) ;
Test6 t2 = new Test6 ( ) ;
Test6 t3 = new Test6 ( ) ;
ExecutorService ser = Executors . newFixedThreadPool ( 3 ) ;
Future < Boolean > r1 = ser. submit ( t1) ;
Future < Boolean > r2 = ser. submit ( t2) ;
Future < Boolean > r3 = ser. submit ( t3) ;
boolean rs1 = r1. get ( ) ;
boolean rs2 = r2. get ( ) ;
boolean rs3 = r3. get ( ) ;
System . out. println ( rs1) ;
System . out. println ( rs2) ;
System . out. println ( rs3) ;
ser. shutdownNow ( ) ;
}
}
练习
示例:买火车票(会发生并发问题,后期解决)
package com. xct. test ;
public class Test4 implements Runnable {
private Integer ticket = 10 ;
@Override
public void run ( ) {
while ( true ) {
if ( ticket<= 0 ) {
break ;
}
try {
Thread . sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "买到了第" + ticket-- + "票" ) ;
}
}
public static void main ( String [ ] args) {
Test4 test4 = new Test4 ( ) ;
new Thread ( test4, "小明" ) . start ( ) ;
new Thread ( test4, "老师" ) . start ( ) ;
new Thread ( test4, "黄牛" ) . start ( ) ;
}
}
示例:龟兔赛跑
package com. xct. test ;
public class Test5 implements Runnable {
private static String winner;
@Override
public void run ( ) {
for ( int i = 0 ; i <= 100 ; i++ ) {
if ( Thread . currentThread ( ) . getName ( ) . equals ( "兔子" ) && i% 10 == 0 ) {
try {
Thread . sleep ( 1 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
boolean flag = getWinner ( i) ;
if ( flag) {
break ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "跑了第" + i+ "步" ) ;
}
}
public boolean getWinner ( int step) {
if ( winner != null ) {
return true ;
} else {
if ( step == 100 ) {
winner = Thread . currentThread ( ) . getName ( ) ;
System . out. println ( "winner是" + winner) ;
return true ;
}
}
return false ;
}
public static void main ( String [ ] args) {
Test5 test5 = new Test5 ( ) ;
new Thread ( test5, "兔子" ) . start ( ) ;
new Thread ( test5, "乌龟" ) . start ( ) ;
}
}