多线程
1.1什么是线程
1.2普通方法调用和多线程
1.3程序、进程、线程
program:程序,(一个没有执行的程序)
Process:进程,(一个程序执行的过程)
Thread:线程(一个进程能够存在多个线程)
1.4核心
2.1线程的创建
Thread class:继承Thread类
Runable接口:实现Runable接口
Callable接口:实现Callable接口
2.2Thread类
//创建多线程第一个方法:继承Thread类
public class ThreadTest extends Thread{
//重写run()方法
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法执行"+i);
}
}
//main方法线程体,是默认执行的线程
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();//调用start方法启动多线程,
// threadTest.run();//run方法只是启动的方法不是线程
for (int i = 0; i < 1000; i++) {
System.out.println("main线程方法执行"+i);
}
}
Thread实现网络下载图片
public class ThreadTest1 extends Thread{
private String url;//图片路径
private String name;//下载名
//创建构造器
public ThreadTest1(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
DownLoad downLoad = new DownLoad();
downLoad.download(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
//创建三个线程
ThreadTest1 t1 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/40f0aea99f5ce22cee00da5fde4c9b18.png", "1.png");
ThreadTest1 t2 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/a3b495a477ed4b140c1412952063e063.png", "2.png");
ThreadTest1 t3 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/85f18c52153fc557f8edda98bd71a491.png", "3.png");
//同时启动三个线程
t1.start();
t2.start();
t3.start();
}
}
//创建一个自动下载网上图片的方法
class DownLoad{
public void download(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("download出现问题");
}
}
}
2.3实现Runable接口
实现runnable接口与继承Thread类的区别在线程启动是,Runnable线程的启动需要new一个Thread对象作为代理
public class RunnableTest implements Runnable{
//重写run()方法
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法执行"+i);
}
}
//main方法线程体,是默认执行的线程
public static void main(String[] args) {
//创建一个runnabletest对象
RunnableTest runnableTest = new RunnableTest();
//创建一个Thread对象,并用start启动这个Thread
new Thread(runnableTest).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main线程方法执行"+i);
}
}
}
利用runnable接口实现下载
public class RunnableTest1 implements Runnable{
private String url;//图片路径
private String name;//下载名
//创建构造器
public RunnableTest1(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
DownLoad downLoad = new DownLoad();
downLoad.download(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
//创建三个线程
ThreadTest1 t1 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/40f0aea99f5ce22cee00da5fde4c9b18.png", "1.png");
ThreadTest1 t2 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/a3b495a477ed4b140c1412952063e063.png", "2.png");
ThreadTest1 t3 = new ThreadTest1("https://img-blog.csdnimg.cn/img_convert/85f18c52153fc557f8edda98bd71a491.png", "3.png");
//同时启动三个线程,用thread类代理的模式启动
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
//创建一个自动下载网上图片的方法
class DownLoad1{
public void download(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("download出现问题");
}
}
}
2.4Thread类与Runnable接口对比
模拟抢票
//多线程并发展示 抢票机制 多个用户共同抢五十张票
//问题:多个线程在操作同一个资源的情况下,会出现出票重复问题,线程不安全,数据紊乱
public class RunnableTest2 implements Runnable{
//定义五十张票
private int ticketnums=200;
//重写run方法,多线程执行方法
@Override
public void run() {
//多线程执行体
while (true){
if(ticketnums==0){
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketnums--+"张票");
}
}
//main方法体
public static void main(String[] args) {
//创建一个要执行的多线程对象
RunnableTest2 ticket = new RunnableTest2();
//创建多个Thread对象进行并发测试,多个Thread会同时访问ticket这一个资源
new Thread(ticket,"黄牛").start();
new Thread(ticket,"小明").start();
new Thread(ticket,"小王").start();
}
}
模拟龟兔赛跑
///龟兔赛跑
public class RaceTest implements Runnable{
private static String winner;//设置一个存放胜利者的属性
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//让兔子睡一会
if(Thread.currentThread().getName()=="兔子"&&i%10==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//测试比赛是否结束
boolean b = gameOver(i);
//比赛结束后停止
if(b){
break;
}
System.out.println(Thread.currentThread().getName()+"跑到了第"+i+"步");
}
}
//游戏结束方法
public boolean gameOver(int race){
//如果已经有胜利者就返回胜利者
if (winner!=null){
//已经有胜利者了
return true;
}
{
//如果谁先等于一百步,那么胜利者就是谁
if (race>=100){
winner=Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}
return false;
}
//main方法
public static void main(String[] args) {
//创建线程对象
RaceTest raceTest = new RaceTest();
//用两个线程去抢占对象
new Thread(raceTest,"兔子").start();
new Thread(raceTest,"乌龟").start();
}
}
2.5实现Callable接口
Callable的优点
- 可以定义返回值
- 可以抛出异常
- 启动线程的步骤较多
- 1.创建要执行的线程对象
- 2.用Executors创建线程池,需要多少个线程就写几
- 3.将创建的线程提交到线程池执行
- 4.获得返回结果
- 5.关闭服务