本章篇幅主要记录多线程编程相关的知识,如有纰漏,望指出。
话不多说,正式开启多线程之旅...
目录
使用方法:继承Thread类,不建议使用,避免OOP单继承局限性
示例:多线程下载网络图片
首先,百度搜索导入commonsio.jar,并将jar包导入项目中。
-
- /**
- * 封装下载器
- */
- public class FileDownload {
- public void download(String url, String fileName) {
- try {
- FileUtils.copyURLToFile(new URL(url), new File(fileName));
- } catch (IOException e) {
- System.out.println("IO异常FileDownload");
- }
- }
- }
-
- /**
- * 异步下载网络图片
- */
- public class LessonThread1 extends Thread {
- private String url;
- private String fileName;
-
- public LessonThread1(String url, String fileName) {
- this.url = url;
- this.fileName = fileName;
- }
-
- @Override
- public void run() {
- FileDownload fileDownload = new FileDownload();
- fileDownload.download(url, fileName);
- System.out.println(fileName + "图片下载完成");
- }
- }
-
- /**
- * 执行下载
- */
- public class Application {
- public static void main(String[] args) {
- new LessonThread1("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "D:\\code\\JavaProject\\Files\\1.jpg").start();
- }
-
- }
使用方法:实现Runnable接口,推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
示例:龟兔赛跑
- public class Race implements Runnable {
- private String winner;
-
- @Override
- public void run() {
- for (int i = 0; i <= 100; i++) {
-
- if (Thread.currentThread().getName().contains("兔子") && i % 10 == 0) {
- try {
- Thread.sleep(1);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- if (gameOver(i)) {
- break;
- }
-
- System.out.println(Thread.currentThread().getName() + "--->跑了" + i + "步");
- }
-
- }
-
- private boolean gameOver(int i) {
- if (winner != null) {
- return true;
- }
-
- if (i >= 100) {
- winner = Thread.currentThread().getName();
- System.out.println("winner is " + winner);
- return true;
- }
- return false;
- }
- }
-
-
- public class Application {
- public static void main(String[] args) {
- Race race = new Race();
- Thread t1 = new Thread(race, "小兔子");
- Thread t2 = new Thread(race, "乌龟");
- t1.start();
- t2.start();
-
- }
- }
使用方法略
1、建议线程正常停止--->利用次数,不建议死循环;
2、建议使用标志位进行终止变量 ,当flag= false时终止线程运行--->设置一个标志位;
3、建议使用Thread.interrupt()标志位,原理同方法2;
4、不要使用stop或者destroy等过时API;
- public class StopThread implements Runnable {
-
- private boolean flag = true;
-
- @Override
- public void run() {
- int i = 0;
- while (flag) {
- System.out.println("run thread num --->" + i++);
- }
- }
-
- private void stop() {
- flag = false;
- }
-
- public static void main(String[] args) {
- StopThread stopThread = new StopThread();
- new Thread(stopThread).start();
- for (int i = 0; i < 1000; i++) {
- System.out.println("main thread num --->" + i);
- if (i == 900) {
- stopThread.stop();
- System.out.println("StopThread线程该停止了");
- }
- }
- }
- }
- public class SleepThread {
- public static void main(String[] args) {
- try {
- sleep();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static void sleep() throws InterruptedException {
- int i = 10;
- while (true) {
- System.out.println(i--);
- Thread.sleep(1_000);
- if (i <= 0) {
- break;
- }
- }
- }
暂停自己的线程,和其他的线程重新竞争cpu资源
插队的线程优先执行,且插队线程执行完成后,其他线程才能继续执行。
Thread.setPriority(),范围从低到高是1-10
优先级越高,大概率会优先执行。
线程默认优先值为5
1、线程分为用户线程和守护线程;
2、虚拟机必须确保用户线程执行完毕,但不用等待守护线程执行完毕;
- public class DaemonThread {
-
- public static void main(String[] args) {
- You you = new You();
- God god = new God();
-
-
- Thread threadGod = new Thread(god, "上帝");
- threadGod.setDaemon(true);//设置守护线程
- threadGod.start();
-
-
- new Thread(you, "你").start();
-
- }
- }
-
- class You implements Runnable {
- @Override
- public void run() {
- for (int i = 0; i < 30_000; i++) {
- System.out.println("开心的活着");
- }
-
- }
- }
-
- class God implements Runnable {
- @Override
- public void run() {
- while (true) {
- System.out.println("上帝保佑着你");
- }
- }
- }
多个线程操作同一个资源,就会造成数据混乱。
锁的对象:变化的量(并发时共同修改的那个变量)
有两种用法:synchronized方法和synchronized块
缺陷:如果方法加锁会大大影响效率
- public class UnsafeList {
- public static void main(String[] args) {
- List<String> strings = new ArrayList<>();
-
- for (int i = 0; i < 10000; i++) {
- new Thread() {
- @Override
- public void run() {
- //锁代码块,锁的对象是多线程要修改的公共变量
- synchronized (strings) {
- strings.add(Thread.currentThread().getName());
- }
-
- }
- }.start();
- }
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println(strings.size());
- }
- }
- public class TestJUC {
- public static void main(String[] args) {
- //位于java.util.concurrent包
- CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
- for (int i = 0; i < 10000; i++) {
- new Thread(() -> {
- strings.add(Thread.currentThread().getName());
- }).start();
- }
-
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- System.out.println(strings.size());
- }
- }
Lock只能锁代码块,synchronized可以锁代码块和方法
- try{
- lock.lock();
- //代码逻辑
- }finally{
- lock.unLock();
- }
定义:多个线程互相持有对方需要的资源,然后行程僵持的状态。
产生的条件:
1、一个资源每次只能被一个线程使用;
2、一个线程因请求资源而阻塞时,对已获得的资源保持不放;
3、线程已获得的资源,在未使用之前,不能强行剥夺;
4、若干线程之间形成一种头尾相接的循环等待资源关系;
-
- public class DeadLock {
- public static void main(String[] args) {
- new Makeup(0, "灰姑凉").start();
- new Makeup(1, "白雪公主").start();
- }
-
- }
-
- class Lipstick {
- //口红
- }
-
- class Mirror {
- //镜子
- }
-
- class Makeup extends Thread {
-
- static Lipstick lipstick = new Lipstick();
- static Mirror mirror = new Mirror();
-
- private int choice;
- private String girlName;
-
- Makeup(int choice, String girlName) {
- this.choice = choice;
- this.girlName = girlName;
- }
-
- @Override
- public void run() {
- makeup();
- }
-
- private void makeup() {
- if (choice == 0) {
- synchronized (lipstick) {
- System.out.println(girlName + "持有口红锁");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- synchronized (mirror) {
- System.out.println(girlName + "持有镜子");
- }
- }
- } else if (choice == 1) {
- synchronized (mirror) {
- System.out.println(girlName + "持有镜子");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- synchronized (lipstick) {
- System.out.println(girlName + "持有口红锁");
- }
- }
- }
- }
- }
ExecutorService和Executors
ExecutorService:真正的线程池接口
Executors:工具类、线程池的工厂类
end