Java学习之多线程
线程的三种创建线程
- Thread class: 继承实现线程类(不建议使用)
- Runnable接口:实现接口(推荐使用,避免单继承局限性)
- Callable接口: 实现接口(了解)
Thread Class 实现方式
1.创建一个类,用于继承Thread类,重写里面的run()方法。
2.创建线程对象,调用启动线程。
3.线程由cpu调度实现重写的run()方法。
public class Demo1 extends Thread{
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("继承线程类,重写线程体:就是重写线程类里的run()方法");
}
}
}
public class Test1 {
public static void main(String[] args) {
//创建线程对象
Demo1 demo1 = new Demo1();
//启动线程
demo1.start();
for (int i = 0; i < 2000; i++) {
System.out.println("我是主方法");
}
}
}
普通方法调用和多线程调用
线程执行时,主线程也会继续执行。
线程开启不一定马上执行,由cpu调度安排。
实现多线程下载网图
1.安装commons-io.jar包,在项目下新建lib文件夹,拷贝到lib,右键包,选择Add a library.此时包就可以使用
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class WebDownload {
public static void main(String[] args) {
Image i1 = new Image("https://image.baidu.com/search/detail?","1");
Image i2 = new Image("https://image.baidu.com/search/detail?" ,"2");
Image i3 = new Image("https://image.baidu.com/search/detail?","3");
i1.start();
i2.start();
i3.start();
}
}
//图片信息类,实现线程继承
class Image extends Thread{
private String url;
private String name;
@Override
public void run() {
Download download = new Download();
download.down(url,name);
System.out.println("下载了"+name);
}
public Image(String url, String name) {
this.url = url;
this.name = name;
}
}
//创建一个下载器
class Download{
public void down(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,方法出现问题");
}
}
}
代码逻辑:
- 首先创建了一个类完成下载器功能,调用commons-io.jar的文件工具类实现了用url下载网络图片的方法。
- 创建线程对象类,Image继承Thread类,定义了图片所需参数,重写run()方法,在方法体中创建了下载器对象,调用了下载的方法。
- 在启动类中创建多个线程对象,启动线程。
Runnable接口实现方式
声明Runnable接口实现类,实现run()方法,创建实现类实例,当成参数传递给线程并启动。
public class Demo1 implements Runnable{
//实现run()方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("实现Runnable接口");
}
}
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
//以下可以简化:new Thread(demo1).start();
Thread thread = new Thread(demo1);//Thread中传入接口实现类对象是因为Thread中有这个构造方法
thread.start();
}
}
多个线程实现同种对象
案例:龟兔赛跑
/**
* 多线程实现同种对象
* 线程同步问题,数据紊乱。
*/
public class Test1 implements Runnable{
int num =10;//票数
@Override
public void run() {
while (true){
if(num<=0)break;
//模拟网络延时可以方法问题的发生性
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num--+"张票");
}
}
public static void main(String[] args) {
Test1 test = new Test1();
new Thread(test,"小红").start();
new Thread(test,"老师").start();
new Thread(test,"黄牛").start();
}
}
实现静态代理与Thread线程对比
//静态代理也需要真实对象,但是通过代理可以实现更多的事:婚庆案例
Lamda表达式
增强for遍历:
//格式:for(元素的数据类型 变量名:数组或者集合)
for(String s:list){
sout(s);
}
//将数组list里的元素一个个取值出来赋给s
//idea快捷键:数组名.for
Lambda表达式的作用:简化匿名内部类,但并不是所有的匿名内部类都可以简化,只有函数式匿名内部类可以
/**
* 函数式编程思想:忽略面向对象的复杂语法,强调做什么,而不是谁去做。强调方法体,而不是对象
* Lambda表达式格式
* ()-> {}
* ():对应着方法的形参
* ->:固定格式
* {}:对应着方法的方法体
*/
public class Demo1 {
public static void main(String[] args) {
//匿名内部类
method(new Swing(){
@Override
public void swimming(){
System.out.println("游泳");
}
});
//简化
method(()->{
System.out.println("继续游泳");
});
}
private static void method(Swing s) {
s.swimming();
}
}
interface Swing{
public abstract void swimming();
}
对于函数式接口,可以通过lambda表达式来创建该接口的对象
/**
* 推导Lambda表达式
*/
public class LambdaTest1 {
//3.静态内部类
static class FunTrue2 implements Fun{
@Override
public void function1() {
System.out.println("I Love Lambda~~~");
}
}
public static void main(String[] args) {
//实现类的实现方式
new FunTrue().function1();
//静态内部类的实现方式
new FunTrue2().function1();
//4.局部内部类
class FunTrue3 implements Fun{
@Override
public void function1() {
System.out.println("I Love Lambda!!!");
}
}
//局部内部类的实现方式
new FunTrue3().function1();
//5.匿名内部类
new Fun(){
@Override
public void function1() {
System.out.println("I Love ...");
}
}.function1();
//用Lambda简化:表达式只能简化函数式接口的匿名内部类写法
Fun like = () -> {
System.out.println("Lambda");
};
like.function1();
}
}
//1.定义一个函数式接口
interface Fun{
void function1();
}
//2.实现类
class FunTrue implements Fun{
@Override
public void function1() {
System.out.println("I Love Lambda!");
}
}
线程的生命周期
线程停止建议使用自定义标识位,不要使用线程类的关闭。
Start()是让线程进入就绪态,而不是调度。
new线程是创建线程。
线程休眠:sleep();每个线程都有一个锁,sleep不会释放锁。
线程礼让:不一定成功。让当前正在执行的线程暂停,而不是阻塞。yield();
线程强制执行:Join方法。可以想象成插队。
守护线程与普通线程
线程同步
多个线程操作同一个资源:并发
线程池
实现重复利用,便于线程管理。