多线程学习
-
程序:是一个静态的概念,是指令和数据的有序集合;
-
进程:是一个动态的概念,是执行一次程序的过程,是系统资源分配的单位
-
线程:是电脑CPU调度和执行的基本单位,每一个进程中至少得有一个线程,且每个进程可以含有多个线程
-
单核同一时间只能执行一个线程,线程开启不一立即执行,由CPU调度执行
三种线程创建方式一:继承Thread类(重点)
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
-
两条线程是同时执行的,当CPU单核时,会将线程穿插执行
-
run()方法和start()方法不一样,run()方法会优先执行完毕后,再返回主线程,而start()方法会开辟新的线程,与main函数主线程同时执行
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; // 练习多线程同步下载文件、图片 public class TestThread2 extends Thread{ private String url; //网络图片地址 private String name; //保存的文件名 public static void main(String[] args){ TestThread2 testThread2 = new TestThread2(url,name); } public TestThread2(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); } } // 下载器 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异常,downloader方法出现异常!"); } } }
三种线程创建方式一:实现Runnable接口(重点)
-
Runnable实现接口与继承Thread差不多,都需要重写run()方法
-
但是实现Runnable接口这种方法需要先创建Runnable接口的实现类对象,再将该对象放到线程中
1)详细写法
ThreadConcurrent threadConcurrent = new ThreadConcurrent(); Thread thread =new Thread(threadConcurrent); thread.start();
2)简写
ThreadConcurrent threadConcurrent = new ThreadConcurrent(); new Thread(threadConcurrent).start();
3)线程并发问题,有可能出现数据混乱的情况
// 线程并发问题 public class ThreadConcurrent implements Runnable{ private int num = 10; public static void main(String[] args) { ThreadConcurrent threadConcurrent = new ThreadConcurrent(); new Thread(threadConcurrent,"小明").start(); new Thread(threadConcurrent,"小红").start(); new Thread(threadConcurrent,"小绿").start(); } @Override public void run() { while (true){ if(num <= 0 ){ break; } System.out.println(Thread.currentThread().getName() + "拿到了第"+ num-- + "票" ); } } }
4)手动控制线程休眠:Thread.sleep(200);
import com.sun.org.apache.xml.internal.utils.RawCharacterHandler; // 线程问题,模拟龟兔赛跑 public class ThreadRace implements Runnable{ // 胜利者 private static String winner; @Override public void run() { for (int i = 0 ; i <= 100 ; i++){ // 模拟兔子休息 if(Thread.currentThread().getName().equals("兔子") && i == 99){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } // 判断比赛是否结束 boolean flag = judgeWinner(i); if (flag) break; System.out.println(Thread.currentThread().getName()+"->跑了"+i+"步!"); } } // 判断是否完成比赛 private boolean judgeWinner(int step){ if (winner !=null){ return true; }else if(step >= 100) { winner = Thread.currentThread().getName(); System.out.println("Winner is " + winner); return true; } return false; } public static void main(String[] args) { ThreadRace threadRace = new ThreadRace(); new Thread(threadRace,"兔子").start(); new Thread(threadRace,"乌龟").start(); } }
三种线程创建方式一:实现Callable接口(了解)
-
继承Thread类,和实现Runnable接口的线程实现方法没有返回值,而实现Callable接口需要返回值以及其类型
-
需要重写call方法以及抛出异常
-
实现流程:
-
创建目标对象;
-
创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交执行:
Future<Boolean> result1 = ser.submit(t1);
-
获取结果:
boolean r1 = result1.get()
-
关闭服务:
ser.shutdownNow();
-
import java.util.concurrent.*; // 线程创建方式三:实现callable接口 public class TestThread3 implements Callable<Boolean>{ public static void main(String[] args) throws ExecutionException, InterruptedException { //创建接口的实现类对象 TestThread3 thread1 = new TestThread3(); TestThread3 thread2 = new TestThread3(); TestThread3 thread3 = new TestThread3(); //创建执行服务,有几个线程,后面就填多少数字 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(thread1); Future<Boolean> r2 = ser.submit(thread2); Future<Boolean> r3 = ser.submit(thread3); // 获取结果 boolean judge1 = r1.get(); boolean judge2 = r2.get(); boolean judge3 = r3.get(); // 关闭服务 ser.shutdownNow(); } @Override public Boolean call() throws Exception { for (int i = 0 ; i< 100 ;i++){ System.out.println(Thread.currentThread().getName()+"->通关"+i+"次!"); } return true; } }
-
Callable优势:
-
可以定义返回值;
-
可以抛出异常;
-
静态代理
-
静态代理的代码实现逻辑即线程的底部代码实现逻辑
import java.lang.annotation.Target; // 静态代理 // 静态代理总结: // 真实对象和代理对象都要实现同一个接口 // 代理对象需要代理真实角色 public class StaticProxy { public static void main(String[] args) { System.out.println("自己操作!"); You you = new You(); you.marry(); System.out.println("代理操作"); WeddingCompany weddingCompany = new WeddingCompany(you); weddingCompany.marry(); // 线程和静态代理对比 new Thread(()->System.out.println("对比!")).start(); new WeddingCompany(you).marry(); } } interface Marry{ void marry(); } // 真实角色 class You implements Marry{ @Override public void marry() { System.out.println("Very Good!"); } } // 代理角色 class WeddingCompany implements Marry{ // 被代理人:真实目标 private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void marry() { before(); this.target.marry(); //真实对象的操作 after(); } private void after() { System.out.println("收尾"); } private void before() { System.out.println("布置现场"); } }