Java14-多线程

https://www.nowcoder.com/tutorial/94/ae05554a3ad84e42b6f9fc4d52859dc4

https://how2j.cn/frontroute
https://how2j.cn/k/thread/thread-start/353.html

多进程

https://www.cnblogs.com/aademeng/articles/6141627.html

public class MultiProcessExample {
    public static void main(String[] args) {
        // 创建进程执行任务
        ProcessBuilder processBuilder1 = new ProcessBuilder("java", "Task1");
        ProcessBuilder processBuilder2 = new ProcessBuilder("java", "Task2");

        try {
            // 启动进程
            Process process1 = processBuilder1.start();
            Process process2 = processBuilder2.start();

            // 等待进程执行结束
            int exitCode1 = process1.waitFor();
            int exitCode2 = process2.waitFor();

            System.out.println("任务1执行结果:" + exitCode1);
            System.out.println("任务2执行结果:" + exitCode2);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Task1 {
    public static void main(String[] args) {
        System.out.println("任务1开始执行");
        // 执行具体任务...
        System.out.println("任务1执行结束");
        System.exit(0); // 退出进程并返回状态码
    }
}

class Task2 {
    public static void main(String[] args) {
        System.out.println("任务2开始执行");
        // 执行具体任务...
        System.out.println("任务2执行结束");
        System.exit(0); // 退出进程并返回状态码
    }
}

线程

进程:系统资源分配的最小单位
线程:程序执行的最小单位

1 启动线程

3 个办法:
法1 继承 Thread 类
法2 实现 Runnable 接口
法3 匿名类

概念

进程(Processor)
线程(Thread)

启动 LOL.exe 叫进程
又启动 DOTA.exe,就是 2 个进程
【进程内部】是 【线程】

不用多线程

package18个程序_多线程.a1_概念.s1_不用多线程;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a1_概念.s1_不用多线程;

public class test {
    public static void main(String[] args) {
        Hero g = new Hero();
        g.name = "盖伦";
        g.hp = 616;
        g.damage = 50;

        Hero t = new Hero();
        t.name = "提莫";
        t.hp = 300;
        t.damage = 30;

        Hero s = new Hero();
        s.name = "赏金猎人";
        s.hp = 500;
        s.damage = 65;

        Hero m = new Hero();
        m.name = "盲僧";
        m.hp = 455;
        m.damage = 80;

        System.out.println("盖伦攻击提莫!");
        while (!t.isDead()){
            g.attackHero(t);
        }

        System.out.println("\n赏金猎人攻击盲僧!");
        while (!m.isDead()){
            s.attackHero(m);
        }
    }
}

用多线程(法1)

不用多线程,就只能一个英雄攻击完另一个英雄,别的英雄才能攻击
用多线程:

package18个程序_多线程.a1_概念.s2_用多线程;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a1_概念.s2_用多线程;

public class Kill extends Thread{
    private Hero hero1;
    private Hero hero2;

    public Kill(Hero hero1, Hero hero2){
        this.hero1 = hero1;
        this.hero2 = hero2;
    }

    public void run(){
        while (!hero2.isDead()){
            hero1.attackHero(hero2);
        }
    }
}
package18个程序_多线程.a1_概念.s2_用多线程;

public class test {
    public static void main(String[] args) {
        Hero g = new Hero();
        g.name = "盖伦";
        g.hp = 616;
        g.damage = 50;

        Hero t = new Hero();
        t.name = "提莫";
        t.hp = 300;
        t.damage = 30;

        Hero s = new Hero();
        s.name = "赏金猎人";
        s.hp = 500;
        s.damage = 65;

        Hero m = new Hero();
        m.name = "盲僧";
        m.hp = 455;
        m.damage = 80;

        Kill kill1 = new Kill(g, t);
        kill1.start();
        Kill kill2 = new Kill(s, m);
        kill2.start();
    }
}

Runable(法2)

package18个程序_多线程.a2_Runnable;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a2_Runnable;

public class battle implements Runnable{
    private Hero hero1;
    private Hero hero2;

    public battle(Hero hero1, Hero hero2){
        this.hero1 = hero1;
        this.hero2 = hero2;
    }

    public void run(){
        while (!hero2.isDead()){
            hero1.attackHero(hero2);
        }
    }
}
package18个程序_多线程.a2_Runnable;

public class test {
    public static void main(String[] args) {
        Hero g = new Hero();
        g.name = "盖伦";g.hp = 616;g.damage = 50;

        Hero t = new Hero();
        t.name = "提莫";t.hp = 300;t.damage = 30;

        Hero s = new Hero();
        s.name = "赏金猎人";s.hp = 500;s.damage = 65;

        Hero m = new Hero();
        m.name = "盲僧";m.hp = 455;m.damage = 80;

        battle battle1 = new battle(g, t);
        new Thread(battle1).start();

        battle battle2 = new battle(s, m);
        new Thread(battle2).start();;
    }
}

多线程 - 匿名类(法3)

匿名类 继承 Thread,重写 run,直接在 run 中写业务代码

package18个程序_多线程.a3_多线程_匿名类;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a3_多线程_匿名类;

public class test {
    public static void main(String[] args) {
        Hero g = new Hero();
        g.name = "盖伦";g.hp = 616;g.damage = 50;

        Hero t = new Hero();
        t.name = "提莫";t.hp = 300;t.damage = 30;

        Hero s = new Hero();
        s.name = "赏金猎人";s.hp = 500;s.damage = 65;

        Hero m = new Hero();
        m.name = "盲僧";m.hp = 455;m.damage = 80;

        //匿名类
        Thread t1 = new Thread(){
          public void run(){
              while (!t.isDead()){
                  g.attackHero(t);
              }
          }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run(){
                while (!m.isDead()){
                    s.attackHero(m);
                }
            }
        };
        t2.start();
    }
}

练习

回顾 IO 综合练习
多线程查找文件内容

原练习思路:
遍历所有文件,当遍历到文件是 .java 时,查找其内容,查完后,再遍历下一个文件

多线程思路:
遍历所有文件,当遍历到文件是.java 时,创建线程查找其内容,不必等该线程结束,继续遍历下一个文件

主线程、子线程是并行的
用 join(),就会变成串行的
当主线程调用子线程 join() 时,子线程执行完后,主线程才开始执行
【上面这个】用于统计用时
不然统计不了,时间总输出于第一行

继承 Thread

package18个程序_多线程.练习.继承Thread;

import java.io.*;

public class ThreadInter extends Thread{
    public File file;
    public String mysearch;

    public ThreadInter(File file, String mysearch){
        this.file = file;
        this.mysearch = mysearch;
    }

    public void run(){
        char[] dates = new char[(int) file.length()];
        try (
                BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
        ){
            bufferedReader.read(dates);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (String.valueOf(dates).contains(mysearch)){
            System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
        }
    }
}
package18个程序_多线程.练习.继承Thread;

import java.io.File;

public class test {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        String path = "src\\第18个程序_多线程\\练习\\src";
        File file = new File(path);
        if(!file.exists()){
            System.out.println("该文件夹不存在!");
            return;
        }
        search(file, "File");
        long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - start) + " ms");
    }

    public static void search(File folder, String mysearch) throws InterruptedException {
        File[] files = folder.listFiles();
        for (File file:files){
            if(file.isDirectory()){
                search(file, mysearch);
            }else if (file.isFile() && file.getName().endsWith(".java")){
                ThreadInter threadInter = new ThreadInter(file, mysearch);
                threadInter.start();
                threadInter.join();
            }
        }
    }
}

Runnable 实现

package18个程序_多线程.练习.Runnable方法;

import java.io.*;

public class RunnableInter implements Runnable{
    public File file;
    public String mysearch;

    public RunnableInter(File file, String mysearch){
        this.file = file;
        this.mysearch = mysearch;
    }

    @Override
    public void run() {
        char[] dates = new char[(int) file.length()];
        try (
            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
            ){
            bufferedReader.read(dates);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (String.valueOf(dates).contains(mysearch)){
            System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
        }
    }
}
package18个程序_多线程.练习.Runnable方法;

import java.io.File;

public class test {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        String path = "src\\第18个程序_多线程\\练习\\src";
        File file = new File(path);
        if(!file.exists()){
            System.out.println("该文件夹不存在!");
            return;
        }
        search(file, "File");
        long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - start) + " ms");
    }

    public static void search(File folder, String mysearch) throws InterruptedException {
        File[] files = folder.listFiles();
        for (File file:files){
            if(file.isDirectory()){
                search(file, mysearch);
            }else if (file.isFile() && file.getName().endsWith(".java")){
                RunnableInter runnableInter = new RunnableInter(file, mysearch);
                Thread t = new Thread(runnableInter);
                t.start();
                t.join();
            }
        }
    }
}

匿名类 实现

package18个程序_多线程.练习.匿名类方法;

import java.io.*;
import java.util.concurrent.CountDownLatch;

public class test {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        String path = "src\\第18个程序_多线程\\练习\\src";
        File file = new File(path);
        if(!file.exists()){
            System.out.println("该文件夹不存在!");
            return;
        }
        search(file,"File");
        long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - start) + " ms");
    }

    public static void search(File folder, String mysearch) throws InterruptedException {
        File[] files = folder.listFiles();
        for (File file:files){
            if(file.isDirectory()){
                search(file, mysearch);
            }else if (file.isFile() && file.getName().endsWith(".java")){
                Thread t = new Thread(){
                    public void run(){
                        char[] datas = new char[(int) file.length()];
                        try(
                            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
                            ){
                            bufferedReader.read(datas);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (String.valueOf(datas).contains(mysearch)){
                            System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
                        }
                    }
                };
                t.start();
                t.join();
            }
        }
    }
}


不用多线程查找,用时是 7ms,说明所线程在这里会用时变多

2 线程方法(常见)

sleep:当前线程暂停
join:加入到当前线程中
setPriority:线程优先级
yield:临时暂停
setDaemon:守护线程
notify():随机选一个在指定对象上挂起的线程激活
notifyAll():激活指定对象上挂起的所有线程

sleep

Thread.sleep(1000); :当前线程暂停 1000 毫秒(ms) ,其他线程不受影响
对应 InterruptedException 中断异常

	try {
	    Thread.sleep(1000);
	} catch (InterruptedException e) {
	    e.printStackTrace();
}

join

	main(){
	...
	//代码执行到这里,一直是main线程在运行
        try {
            //t1线程加入到 main 线程中来,只有t1线程运行结束,才继续往下走
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
	}

setPriority - 线程优先级

	Thread t1= new Thread(){
        public void run(){

            while(!teemo.isDead()){
                gareen.attackHero(teemo);
            }              
        }
    };
          
    Thread t2= new Thread(){
        public void run(){
            while(!leesin.isDead()){
                bh.attackHero(leesin);
            }              
        }
    };
     
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);
    t1.start();
    t2.start();

yiel - 临时暂停

Thread.yield();

setDaemon - 守护线程

当进程中,所有线程都是守护线程时,结束当前进程
守护线程 常用来做日志,性能统计

例子:
公司有 销售部、生产部
还有后勤、行政
销售部,生产部都解散了,只剩下后勤、行政,这家公司也就解散了

	public static void main(String[] args) {      
        Thread t1= new Thread(){
            public void run(){
                int seconds =0;
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.printf("已经玩了LOL %d 秒%n", seconds++);
                }              
            }
        };
        t1.setDaemon(true);
        t1.start();
    }

练习1

实现:
英雄可以放技能叫做: 波动拳 - a_du_gen
每隔一秒钟,可发一次,只能连续发 3 次
3 次后,要充能 5 秒,再继续发

package18个程序_多线程.a2_常用方法.练习1;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public Hero(){}
    public Hero(String name){
        this.name = name;
    }
    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    public boolean isDead(){
        return hp>0? false:true;
    }

    public void a_du_gen(){
        while (true){
            for (int i = 1; i < 4; i++){
                try {
                    System.out.println(this.name + " 发动技能第 " + i + " 次, 需要充能 1 秒");
                    Thread.sleep(1000); // 充能1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(this.name + "3次攻击后要充能5秒");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package18个程序_多线程.a2_常用方法.练习1;

public class test {
    public static void main(String[] args) {
        Hero hero1 = new Hero("盖伦");
        Hero hero2 = new Hero("提莫");

        Thread thread1 = new Thread(){
            public void run(){
                hero1.a_du_gen();
            }
        };

        Thread thread2 = new Thread(){
            public void run(){
                hero2.a_du_gen();
            }
        };

        thread1.start();
        thread2.start();
    }
}

练习2

1 随机生成长度是 3 的字符串,作为密码
2 创建破解线程,用穷举法,匹配该密码
3 创建日志线程,打印都用过哪些字符串去匹配,这个日志线程为守护线程

package18个程序_多线程.a2_常用方法.练习2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class test {
    public static void main(String[] args) throws InterruptedException {
        char[] c = new char[3];
        for (int i = 0; i < 3; i++){
            c[i] = (char) ((int)(Math.random()*26)+65);
        }
        String s = Arrays.toString(c);
        System.out.println("密码是: " + s + "\n");

        List<String> list = new ArrayList<>();
        Thread thread = new Thread(){
            public void run(){
                char[] temp = new char[3];
                int flag = 0;
                for (char i = 'A'; i <= 'Z'; i++){
                    temp[0] = i;
                    for (char j = 'A'; j <= 'Z'; j++){
                        temp[1] = j;
                        for(char z = 'A'; z <= 'Z'; z++){
                            temp[2] = z;
                            String s1 = Arrays.toString(temp);
                            list.add(s1);
                            if(s1.equals(s)){
                                flag = 1;
                                System.out.println("找到了密码 : " + s1 + "\n");
                            }
                        }
                        if(flag == 1)
                            break;
                    }
                    if (flag == 1)
                        break;
                }
            }
        };

        Thread log = new Thread(){
            public void run(){
                while (true){
                    if (!list.isEmpty()){
                        System.out.println("记录过的密码: " + list.remove(0));
                    }
                    else Thread.yield();
                }
            }
        };

        thread.start();
        thread.join();
        log.setDaemon(true);
        log.start();
        log.join();
    }
}

3 同步(线程安全)

对个数据进行多次加减操作。每次加减 1
如,10000,多次加减,加减有多个线程
加1时得到 10001,此时减得到的是10000,10000-1=9999
这个值就变成了 9999,本来一加一减还是 10000 本身才对
这个 9999 就是【脏数据】

解决办法:
【加线程】运行时是并行运算,【减线程】不被允许访问数据
【减线程】运行时是并行运算,【加线程】不被允许访问数据

synchronized

	Object someObject =new Object();
	synchronized (someObject){
	  // 此处的代码只有占有了 someObject 才能执行
	}

盖伦有 10000 滴血
同时被对方多个英雄攻击 = 有多个线程在减少盖伦的 hp
在基地 = 有多个线程在恢复盖伦的 hp

package18个程序_多线程.a3_同步.s1_加减血量;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public void recover(){
        hp++;
    }

    // 掉血
    public void hurt(){
        hp--;
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a3_同步.s1_加减血量;

public class test {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        Hero g = new Hero("盖伦", 10000);
        System.out.println(g.name + " 初始血量 = " + g.hp + "\n");

        int n = 10000;
        Thread[] addthread = new Thread[n];
        Thread[] reducethread = new Thread[n];

        final Object someObject = new Object();
        for(int i = 0; i < n; i++){
            Thread t1 = new Thread(){
                public void run(){
                    synchronized (someObject){
                        g.recover();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t1.start();
            addthread[i]= t1;
        }

        for(int i = 0; i < n; i++){
            Thread t2 = new Thread(){
                public void run(){
                    synchronized (someObject){
                        g.hurt();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t2.start();
            reducethread[i]= t2;
        }

        // 等待所有的加线程结束
        for (Thread t : addthread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 等待所有的减线程结束
        for (Thread t : reducethread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
        long end= System.currentTimeMillis();
        System.out.println("用时 = " + (end - start) + "ms");
    }
}

Hero 作为对象

	public void hurt(){
        //使用this作为同步对象
        synchronized (this) {
            hp=hp-1;   
        }
    }

完整代码:

package18个程序_多线程.a3_同步.s2_Hero作为同步对象;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public void recover(){
        hp++;
    }

    // 掉血
    public void hurt(){
        synchronized (this){
            hp--;
        }
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a3_同步.s2_Hero作为同步对象;

public class test {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        Hero g = new Hero("盖伦", 10000);
        System.out.println(g.name + " 初始血量 = " + g.hp + "\n");

        int n = 10000;
        Thread[] addthread = new Thread[n];
        Thread[] reducethread = new Thread[n];

        final Object someObject = new Object();
        for(int i = 0; i < n; i++){
            Thread t1 = new Thread(){
                public void run(){
                    synchronized (g){
                        g.recover();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t1.start();
            addthread[i]= t1;
        }

        for(int i = 0; i < n; i++){
            Thread t2 = new Thread(){
                public void run(){
                    g.hurt();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t2.start();
            reducethread[i]= t2;
        }

        // 等待所有的加线程结束
        for (Thread t : addthread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 等待所有的减线程结束
        for (Thread t : reducethread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
        long end= System.currentTimeMillis();
        System.out.println("用时 = " + (end - start) + "ms");
    }
}

相同效果的其他做法

直接在方法前加上修饰符 synchronized
其所对应的同步对象,就是 this
和 hurt 方法达到的效果一样

package18个程序_多线程.a3_同步.s2_Hero作为同步对象.c2_相同效果_方法名前加synchronized;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    // 效果用下面的 【掉血】的以Hero为同步对象 一样!
    public synchronized void recover(){
        hp++;
    }

    // 掉血
    public void hurt(){
        synchronized (this){
            hp--;
        }
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a3_同步.s2_Hero作为同步对象.c2_相同效果_方法名前加synchronized;

public class test {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        Hero g = new Hero("盖伦", 10000);
        System.out.println(g.name + " 初始血量 = " + g.hp + "\n");

        int n = 10000;
        Thread[] addthread = new Thread[n];
        Thread[] reducethread = new Thread[n];

        final Object someObject = new Object();
        for(int i = 0; i < n; i++){
            Thread t1 = new Thread(){
                public void run(){
                    g.recover();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t1.start();
            addthread[i]= t1;
        }

        for(int i = 0; i < n; i++){
            Thread t2 = new Thread(){
                public void run(){
                    g.hurt();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t2.start();
            reducethread[i]= t2;
        }

        // 等待所有的加线程结束
        for (Thread t : addthread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 等待所有的减线程结束
        for (Thread t : reducethread){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
        long end= System.currentTimeMillis();
        System.out.println("用时 = " + (end - start) + "ms");
    }
}

练习

MyStack 类,改为线程安全的类

一个类,其方法都是 synchronized 修饰,该类就是做线程安全的类

package18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;

public class Hero {
    public String name; // 名字
    public float hp; // 护甲
    public int damage; // 攻击力
    public Hero(){}
    public Hero(String name){
        this.name = name;
    }
    public synchronized String toString(){
        return name;
    }
}
package18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;

public interface stack {
    //把英雄推入到最后位置
    public void push(Hero h);

    //把最后一个英雄取出来
    public Hero pull();

    //查看最后一个英雄
    public Hero peek();
}
package18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;

import java.util.Iterator;
import java.util.LinkedList;

public class MyStack implements stack {
    private static LinkedList<Hero> list = new LinkedList<>();

    //把英雄推入到最后位置
    @Override
    public synchronized void push(Hero h) {
        list.addLast(h);
    }

    //把最后一个英雄取出来
    @Override
    public Hero pull() {
        return list.removeLast();
    }

    //查看最后一个英雄
    @Override
    public Hero peek() {
        return list.peekLast();
    }

    public synchronized void get(){
        Iterator<Hero> it= list.iterator();
        System.out.println("英雄有:");
        while(it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println();
    }
}
package18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;

public class test {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.push(new Hero("盖伦"));
        myStack.push(new Hero("提莫"));
        myStack.get();

        System.out.println("把最后一个英雄取出来:");
        System.out.println(myStack.pull());
        myStack.get();

        System.out.println("查看最后一个英雄:");
        System.out.println(myStack.peek());
        myStack.get();
    }
}

4 线程安全

线程安全:对象对应的公共数据区始终正确

HashMap vs Hashtable

HashMap、Hashtable 都实现 Map 接口,都是 键值对保存数据的方式
区别1:
HashMap 可以放 null
Hashtable 不可放 null
区别2:
HashMap 不是线程安全类
Hashtable 是线程安全类

StringBuffer vs StringBuilder

StringBuffer 线程安全
StringBuilder 非线程安全
大量字符串拼接操作时,若是单线程 用 StringBuilder 更快
若是多线程,用 StringBuffer 保证数据安全

Vector vs ArrayList

Vector 线程安全
ArrayList 非线程安全

非线程安全的集合 转 为线程安全 synchronizedList

ArrayList 非线程安全 = 多个线程可同时进入一个 ArrayList.add 方法

	public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = Collections.synchronizedList(list1);
    }

练习

MyStack 类,改为线程安全的类

package18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;

public class Hero {
    public String name; // 名字
    public float hp; // 护甲
    public int damage; // 攻击力
    public Hero(){}
    public Hero(String name){
        this.name = name;
    }
    public String toString(){
        return name;
    }
}
package18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;

public interface stack {
    //把英雄推入到最后位置
    public void push(Hero h);

    //把最后一个英雄取出来
    public Hero pull();

    //查看最后一个英雄
    public Hero peek();
}
package18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;

import java.util.*;

public class MyStack<T>implements stack{
    private static List<Hero> list = Collections.synchronizedList(new LinkedList<Hero>());

    //把英雄推入到最后位置
    @Override
    public void push(Hero h) {
        list.add(h);
    }

    //把最后一个英雄取出来
    @Override
    public Hero pull() {
        return list.remove(list.size() - 1);
    }

    //查看最后一个英雄
    @Override
    public Hero peek() {
        return list.get(list.size()-1);
    }

    public void get() {
        Iterator<Hero> it = list.iterator();
        System.out.println("英雄有:");
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println();
    }
}
package18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;

public class test {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.push(new Hero("盖伦"));
        myStack.push(new Hero("提莫"));
        myStack.get();

        System.out.println("把最后一个英雄取出来:");
        System.out.println(myStack.pull());
        myStack.get();

        System.out.println("查看最后一个英雄:");
        System.out.println(myStack.peek());
        myStack.get();
    }
}

练习

两种方式

implements Runnable
extends Thread

public class Threadtest {
	
	public static void main(String[] args) {         //主方法
		//创建对象c和c1   
		Compute c = new Compute();
		Compute1 c1 = new Compute1();
		//创建线程对象t和t1
		Thread t = new Thread(c);
		Thread t1 = new Thread(c1);
		t.start();                    			      //启动线程对象t
		t1.start();                 				  //启动线程对象t1
		}

}

class Compute1 implements Runnable{
	public void run() {                  			//实现方法run()
		for (int i = 0; i < 10; i++) {
			System.out.println("这个数字是:" + i);
		}
	}

}

class Compute implements  Runnable{
	int i = 0;                             			     //创建成员变量i
	public void run() {                  			     //实现方法run()
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}
	}
	
}
public class Threadtest1 {
	public static void main(String[] args) {	          //主方法
		//创建对象t和t1
		Compute2 t = new Compute2();
		Compute3 t1 = new Compute3();
		//启动对象t和t1
		t.start();
		t1.start();
	}
}

class Compute2 extends Thread {       			      //创建继承线程的类cCompute
	int i = 0;                              		  //创建成员变量i
	public void run() {                     		  //实现方法run()
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}
	}
}

class Compute3 extends Thread {           		            //创建继承线程的类cCompute1
	public void run() {                     		    //实现方法run()
		for (int i = 0; i < 10; i++) {
			System.out.println("这个数字是:" + i);
		}
	}
	
}

implements Runnable好处:适合多个相同程序代码的线程处理同一资源

卖火车票: implements Runnable

public class Example {
    public static void main(String[] args) {
        TicketWindow tw = new TicketWindow();
        new Thread(tw, "窗口1").start();  //创建线程对象并命名窗口1,开启进程
        new Thread(tw, "窗口2").start();
        new Thread(tw, "窗口3").start();
        new Thread(tw, "窗口4").start();
    }
}
class TicketWindow implements Runnable {
    private int tickets = 100;
    public void run() {
        while(true) {
            if(tickets > 0) {
                Thread th = Thread.currentThread();  //获取当前进程
                String th_name = th.getName();
                System.out.println(th_name+"正在发售第"+tickets--"张票");
            }
            System.out.println("MyThread类的run方法在运行");
        }
    }
}

其他方法


线程设置优先级:
线程名.setPriority(MIN_PRIORITY)
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10

sleep(1000); 线程休眠1秒
t.interrupt(); 线程唤醒

yield(); 线程暂停,不会阻塞线程,只是转换为就绪状态

join() 线程进入阻塞状态,例如在线程B中调用线程A的join(),那线程B会进入到阻塞队列,直到线程A结束或中断线程

synchronized 同步

同步块:
Object lock = new Object();
synchronized(lock)
{}

同步方法:
public synchronized int make(){}
package ch12code;

public class st {
    private int[] data = new int[10];
    private int ip=0, op=0, c=0;

    public synchronized void put(int n){
        try {
            while (c == data.length) {
                this.wait();
            }

            data[ip] = n;
            System.out.println(ip + " 放入 " + n);
            ip++;

            if(ip == data.length) {
                ip = 0;
            }
            c++;

            this.notify();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void get(){
        try {
            while (c==0){
                this.wait();
            }

            int t = data[op];
            System.out.println(op + " 取出 " + t);
            data[op] = 0;
            op++;

            if(op==data.length){
                op = 0;
            }
            c--;

            this.notify();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class input implements Runnable{
    private st s;
    private int num=0;

    input(st s){
        this.s = s;
    }

    @Override
    public void run() {
        while (num<20){
            s.put(num++);
        }
    }
}

class output implements Runnable{
    private st s;
    output(st s){
        this.s = s;
    }

    @Override
    public void run() {
        while (true){
            s.get();
        }
    }
}

class test{
    public static void main(String[] args) {
        st s = new st();
        input s1 = new input(s);
        output s2 = new output(s);
        new Thread(s1).start();
        new Thread(s2).start();
    }
}


实践

class ham1{
    static Object box = new Object();      //创建对象box
    static int totalmaterial = 10;         //制作汉堡包的材料属性
    static int sales1 = 0;                  //销售多少个汉堡包属性
    static int sales2=0;
    static int production = 5;             //一共多少汉堡
}

//厨师
class hmaker1 extends Thread {

    //make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
    public void make() {
        synchronized (ham1.box) {     			//创建同步块
            (ham1.production)++;

            System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham1.production - ham1.sales1 - ham1.sales2) + "个)");
            try {
                ham1.box.notify();
            } catch (Exception e) {
            }
        }
    }

    public void run() {
        //使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
        while (ham1.production < ham1.totalmaterial) {
            make();
            try {
                sleep(3000);           			//线程休眠3秒
            } catch (Exception e) {
            }
            make();                   			//调用make()方法
        }
    }
}

// 营业员
class hassistant1 extends Thread{

    public void sell1() {          				//创建营业员卖汉堡包的方法sell1
        //当没有汉堡包的时候
        if (ham1.production == (ham1.sales1 + ham1.sales2)) {
            System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,汉堡包没了!!");
            ham1.sales1 = 0;
            ham1.production = 0;
            try {
                ham1.box.wait();
            } catch (Exception e) {
            }
        }
        (ham1.sales1)++;
        //输出相应信息
        System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham1.sales1+ "个)");
    }

    public void sell2() {      					//创建营业员卖汉堡包的方法sell2
        //当没有汉堡包的时候
        if (ham1.production == (ham1.sales1 + ham1.sales2)) {
            System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,汉堡包没了!!");
            ham1.sales2 = 0;
            ham1.production = 0;
            try {
                ham1.box.wait();
            } catch (Exception e) {
            }
        }
        (ham1.sales2)++;
        //输出相应信息
        System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham1.sales2+ "个)");
    }

    public void run() {         				//重写run()方法
        //当箱子里面有汉堡包里有汉堡包的情况下不断的卖
        while ((ham1.sales1 + ham1.sales2) < ham1.production) {
            sell1();       					//调用sell1()方法
            try {
                sleep(1000);
            } catch (Exception e) {
            }
        }
        while ((ham1.sales1 + ham1.sales2) < ham1.production) {
            try {
                sleep(1000);
            } catch (Exception e) {
            }
            sell2();             			//调用方法sell2()
        }
    }
}

public class t2 {
    public static void main(String[] args) {
        hmaker1 maker = new hmaker1();           	//创建对象maker
        hassistant1 assistant1 = new hassistant1();//创建对象assistant
        hassistant1 assistant2 = new hassistant1();//创建对象assistant
        //对对象maker进行设置
        maker.setName("甲");
        //启动线程

        assistant1.setName("甲");
        assistant2.setName("乙");

        maker.start();
        assistant1.start();
        assistant2.start();
    }
}
//汉堡
class ham2{
    static Object box1 = new Object();      //创建对象box
    static Object box2 = new Object();      //创建对象box
    static int totalmaterial1 = 10;         //制作汉堡包的材料属性
    static int totalmaterial2 = 10;         //制作汉堡包的材料属性
    static int sales11 = 0;                  //销售多少个汉堡包属性
    static int sales12 =0;
    static int sales21 =0;
    static int sales22 =0;
    static int production1 = 5;             //一共多少汉堡
    static int production2 = 5;             //一共多少汉堡
}

//厨师
class hmaker2 extends Thread {

    //make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
    public void make() {
        synchronized (ham2.box1) { 			//创建同步块
            (ham2.production1)++;
            //输出相应的信息
            System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham2.production1 - ham2.sales11 - ham2.sales12) + "个A类汉堡包)");
            try {
                ham2.box1.notify();
            } catch (Exception e) {
            }
        }
    }

    public void run() {           			          //重写run()方法
        //使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
        while (ham2.production1 < ham2.totalmaterial1) {
            make();					            //调用make()方法
            try {
                sleep(3000);				     //线程休眠3秒
            } catch (Exception e) {
            }
            if (ham2.production1 == ham2.totalmaterial1) {
                System.out.println("所有的材料用完了!");
            }
        }
    }
}

class hmaker3 extends Thread {

    //make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
    public void make() {
        synchronized (ham2.box2) { 			//创建同步块
            (ham2.production2)++;
            //输出相应的信息
            System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham2.production2 - ham2.sales21 - ham2.sales22) + "个B类汉堡包)");
            try {
                ham2.box2.notify();
            } catch (Exception e) {
            }
        }
    }

    public void run() {           			          //重写run()方法
        //使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
        while (ham2.production2 < ham2.totalmaterial2) {
            make();					            //调用make()方法
            try {
                sleep(3000);				     //线程休眠3秒
            } catch (Exception e) {
            }
            if (ham2.production2 == ham2.totalmaterial2) {
                System.out.println("所有的材料用完了!");
            }
        }
    }
}

// 营业员
class hassistant2 extends Thread{

    public void sell1() {          				//创建营业员卖汉堡包的方法sell1
        //当没有汉堡包的时候
        if (ham2.production1 == (ham2.sales11 + ham2.sales12)) {
            System.out.println("营业员" + getName() + "asd:顾客朋友们,请稍微等一下,A汉堡包没了!!");
            ham2.sales11 = ham2.sales12 = 0;
            ham2.production1 = 0;
            try {
                ham2.box1.wait();
            } catch (Exception e) {
            }
        }else {
            if (ham2.production1 > (ham2.sales11 + ham2.sales12)) {
                ham2.sales11++;
                ham2.sales21++;
                //输出相应信息
                System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham2.sales11 + "个A汉堡 " + ham2.sales21 + "个B汉堡)");
            }
        }
    }

    public void sell2() {      					//创建营业员卖汉堡包的方法sell2
        //当没有汉堡包的时候
        if (ham2.production2 == (ham2.sales21 + ham2.sales22)) {
            System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,B汉堡包没了!!");
            ham2.sales21 = ham2.sales22 = 0;
            ham2.production2 = 0;
            try {
                ham2.box2.wait();
            } catch (Exception e) {
            }
        }else {
            if (ham2.production2 > (ham2.sales21 + ham2.sales22)) {
                ham2.sales12++;
                ham2.sales22++;
                //输出相应信息
                System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham2.sales12 + "个A汉堡 " + ham2.sales22 + "个B汉堡)");
            }
        }
    }

    public void run() {         				//重写run()方法
        //当箱子里面有汉堡包里有汉堡包的情况下不断的卖
        while ((ham2.sales11 + ham2.sales12) < ham2.production1) {
            try {
                sleep(1000);
            } catch (Exception e) {
            }
            sell1();       					//调用sell1()方法
        }
        while ((ham2.sales21 + ham2.sales22) < ham2.production2) {
            try {
                sleep(1000);
            } catch (Exception e) {
            }
            sell2();             			//调用方法sell2()
        }
    }
}


public class t4 {

    public static void main(String[] args) {
        hmaker2 maker1 = new hmaker2();             //创建对象maker
        hmaker3 maker2 = new hmaker3();            //创建对象maker

        hassistant2 assistant1 = new hassistant2();//创建对象assistant
        hassistant2 assistant2 = new hassistant2();//创建对象assistant

        //对对象maker进行设置
        maker1.setName("甲");
        maker2.setName("乙");
        assistant1.setName("甲");
        assistant2.setName("乙");

        //启动线程
        maker1.start();
        maker2.start();
        assistant1.start();
        assistant2.start();

    }
}

5 死锁

线程1 已占有对象1,试图占有对象2
线程2 已占有对象2,试图占有对象1
线程1 等 线程2 释放对象2
线程2 等 线程1 释放对象1

简单程序

package18个程序_多线程.a5_死锁.s1_简单程序;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public Hero(String name){
        this.name= name;
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a5_死锁.s1_简单程序;

public class test {
    public static void main(String[] args) {
        final Hero j = new Hero("九尾妖狐");
        final Hero a = new Hero("安妮");

        Thread t1 = new Thread(){
            public void run(){
                synchronized (j){
                    System.out.println("t1 已占有九尾妖狐");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 尝试占有 安妮...");
                    System.out.println("等待中...");
                    synchronized (a){
                        System.out.println("do something");
                    }
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run(){
                synchronized (a){
                    System.out.println("t2 已占有安妮");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 尝试占有 九尾妖狐...");
                    System.out.println("等待中...");
                    synchronized (j){
                        System.out.println("do something");
                    }
                }
            }
        };
        t2.start();
    }
}

练习

3 个同步对象 a, b, c
3 个线程 t1, t2, t3
设计 使这 3 个线程彼此死锁

package18个程序_多线程.a5_死锁.练习;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage; // 攻击

    public void attackHero(Hero hero){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hero.hp -= damage;
        System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);

        if (isDead())
            System.out.println(hero.name + " 阵亡!");
    }

    public Hero(String name){
        this.name= name;
    }

    public boolean isDead(){
        return hp>0? false:true;
    }
}
package18个程序_多线程.a5_死锁.练习;

public class test {
    public static void main(String[] args) {
        final Hero j = new Hero("九尾妖狐");
        final Hero m = new Hero("盲僧");
        final Hero t = new Hero("提莫");

        Thread t1 = new Thread(){
            public void run() {
                synchronized (j){
                    System.out.println("t1 已占有九尾妖狐");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 尝试占有 盲僧...");
                    System.out.println("等待中...");
                    synchronized (m){
                        System.out.println("do something");
                    }
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run() {
                synchronized (m){
                    System.out.println("t2 已占有盲僧");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t2 尝试占有 提莫...");
                    System.out.println("等待中...");
                    synchronized (t){
                        System.out.println("do something");
                    }
                }
            }
        };
        t2.start();

        Thread t3 = new Thread(){
            public void run() {
                synchronized (m){
                    System.out.println("t3 已占有提莫");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t3 尝试占有 九尾妖狐...");
                    System.out.println("等待中...");
                    synchronized (j){
                        System.out.println("do something");
                    }
                }
            }
        };
        t3.start();
    }
}


6 交互

线程间有交互通知的需求
如: 2 个线程,处理同一个英雄,加血、减血
减血线程,发现血量=1,就停止减血,直到加血的线程为英雄加血,才能继续减血

反例

不好的做法:
用 while 故意设计减血线程频率更高

package18个程序_多线程.a6_线程交互.s1_反例;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public void recover(){
        hp++;
    }

    // 掉血
    public void hurt(){
        hp--;
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a6_线程交互.s1_反例;

public class test {
    public static void main(String[] args) {
        final Hero g = new Hero("盖伦",10);

        Thread t1 = new Thread(){
          public void run(){
              while (true){
                  while (g.hp == 1){
                      continue;
                  }
                  g.hurt();
                  System.out.println(g.name + "的血量减1, 现血量 = " + g.hp);
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run() {
                while (true){
                    g.recover();
                    System.out.println(g.name + "的血量加1, 现血量 = " + g.hp);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t2.start();
    }
}

wait、notify

this.wait():占有 this 的线程等待,并临时释放占有
this.notify():通知等待 this 的线程,可以苏醒了
this.notifyAll():所有的等待 this 的线程,你们可以苏醒了
this 可以换成其他对象

wait()、notify(),不是 Thread 线程的方法,而是 对象 Object 的方法
所有的 Object 都可以作为同步对象

package18个程序_多线程.a6_线程交互.wait_notify;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public synchronized void recover(){
        hp++;
        System.out.println(name + "的血量加1, 现血量 = " + hp);
        this.notify();// 等待this的线程可以苏醒了
    }

    // 掉血
    public synchronized void hurt(){
        if (hp == 1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hp--;
        System.out.println(name + "的血量减1, 现血量 = " + hp);
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a6_线程交互.wait_notify;

public class test {
    public static void main(String[] args) {
        final Hero g = new Hero("盖伦",10);

        Thread t1 = new Thread(){
          public void run(){
              while (true){
                  g.hurt();
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run() {
                while (true){
                    g.recover();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t2.start();
    }
}

练习1 - 线程交互

加血线程更频繁,英雄的最大血量1000
设计加血、减血线程的交互,
回血回满后,加血线程等待,直到有减血线程减血

package18个程序_多线程.a6_线程交互.练习1_线程交互;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public synchronized void recover(){
        if (hp == 1000){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hp++;
        System.out.println(name + "的血量加1, 现血量 = " + hp);
    }

    // 掉血
    public synchronized void hurt(){
        hp--;
        System.out.println(name + "的血量减1, 现血量 = " + hp);
        this.notify();
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a6_线程交互.练习1_线程交互;

public class test {
    public static void main(String[] args) {
        final Hero g = new Hero("盖伦",940);

        Thread t1 = new Thread(){
            public void run(){
                while (true){
                    g.hurt();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run(){
                while (true){
                    g.recover();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t2.start();
    }
}

练习2 - 多线程交互

加血线程 2 个,减血线程 5 个,同时运行
运行一段时间,观察发生的错误,分析原因,考虑解决办法

package18个程序_多线程.a6_线程交互.练习2_多线程交互;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public synchronized void recover(){
        if (hp == 1000){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hp++;
        System.out.println(name + "的血量加1, 现血量 = " + hp);
    }

    // 掉血
    public synchronized void hurt(){
        hp--;
        System.out.println(name + "的血量减1, 现血量 = " + hp);
        this.notify();
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a6_线程交互.练习2_多线程交互;

import java.util.ArrayList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        final Hero g = new Hero("盖伦",940);

        List<Thread> list1 = new ArrayList<>();
        List<Thread> list2 = new ArrayList<>();

        for (int i = 0; i < 2; i++){
            Thread t1 = new Thread(){
                public void run(){
                    while (true){
                        g.hurt();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            list1.add(t1);
        }

        for (int i = 0; i < 5; i++){
            Thread t2 = new Thread(){
                public void run(){
                    while (true){
                        g.recover();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            list2.add(t2);
        }

        for (Thread t1 : list1){
            t1.start();
//                t1.join();
        }

        for (Thread t2 : list2){
            t2.start();
//                t2.join();
        }
    }
}

问题是:
血量会超过 1000!

不好的解决办法:
把 Hero.hurt() 方法中,this.notify(); 注释掉

这样加血线程都停掉,最后血量变成负数

真正的解决办法:

package18个程序_多线程.a6_线程交互.练习2_多线程交互.改进;

public class Hero {
    public String name;
    public float hp; // 血量
    public int damage;

    public Hero(String name, float hp){
        this.name = name;
        this.hp = hp;
    }

    // 回血
    public synchronized void recover(){
        if (hp >= 1000){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hp++;
        System.out.println(name + "的血量加1, 现血量 = " + hp);
//        this.notify();
    }

    // 掉血
    public synchronized void hurt(){
        if (hp <= 1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        hp--;
        System.out.println(name + "的血量减1, 现血量 = " + hp);
    }

    public void attackHero(Hero hero){
        hero.hp -= damage;
        System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
        if(hero.isDead()){
            System.out.println(hero.name + "阵亡!");
        }
    }

    public boolean isDead(){
        return hp>0?false:true;
    }
}
package18个程序_多线程.a6_线程交互.练习2_多线程交互.改进;

import java.util.ArrayList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        final Hero g = new Hero("盖伦",940);

        List<Thread> list1 = new ArrayList<>();
        List<Thread> list2 = new ArrayList<>();

        for (int i = 0; i < 5; i++){
            Thread t1 = new Thread(){
                public void run(){
                    while (true){
                        g.hurt();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            list1.add(t1);
        }

        for (int i = 0; i < 2; i++){
            Thread t2 = new Thread(){
                public void run(){
                    while (true){
                        g.recover();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            list2.add(t2);
        }

        for (Thread t1 : list1){
            t1.start();
//                t1.join();
        }

        for (Thread t2 : list2){
            t2.start();
//                t2.join();
        }
    }
}

练习3 - 生产者、消费者

1 用栈存放数据
1.1 栈是线程安全
1.2 栈里的数据是 0 时,访问 pull 的线程等待
栈里的数据有 200 个时,访问 push 的线程等待

2 生产者(Producer)线程类,生产【随机大写字符】压入栈
3 消费者(Consumer)线程类,从堆栈中弹出字符并打印到控制台
4 测试类,使 2 个生产者、3 个消费者线程同时运行,结果类似如下 :

package18个程序_多线程.a6_线程交互.练习3_生产者_消费者;

public interface stack {
    public void push(char c); // 入栈
    public char peek(); // 查看栈顶元素
    public char pop(); // 出栈
    public int size(); // 返回栈的元素个数
}
package18个程序_多线程.a6_线程交互.练习3_生产者_消费者;

import java.util.EmptyStackException;
import java.util.Vector;

public class MyStack implements stack{
    Vector<Character> vector = new Vector<Character>(); // Vector 是线程安全

    @Override // 入栈
    public synchronized void  push(char c) {
        vector.addElement(c);
    }

    @Override  // 查看栈顶元素
    public synchronized char peek() {
        int size = vector.size();
        if (size == 0)
            throw  new EmptyStackException();
        return vector.get(size-1);
    }

    @Override
    public synchronized char pop() {
        int size = vector.size();
        char c = peek();
        vector.removeElement(size-1);
        return c;
    }

    @Override
    public synchronized int size() {
        return vector.size();
    }
}
package18个程序_多线程.a6_线程交互.练习3_生产者_消费者;

import java.util.Random;

public class Producer extends Thread{
    MyStack stack = new MyStack();
    String name;
    public Producer(){}
    public Producer(String name){
        this.name = name;
    }


    public synchronized void run(){
        Random r = new Random();
        char t;
        while(true){
            t = (char) (r.nextInt(27) + 65);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while (stack.size() == 200){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            stack.push(t);
            System.out.println(name + " 压入 " + t);
            this.notify();
        }
    }
}
package18个程序_多线程.a6_线程交互.练习3_生产者_消费者;

public class Consumer extends Thread{
    MyStack stack = new MyStack();
    String name;
    public Consumer(){}
    public Consumer(String name){
        this.name = name;
    }
    public synchronized void run(){
        char t;
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while (stack.size() == 0){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            t = stack.pop();
            System.out.println(name + " 弹出 " + t);
            this.notify();
        }
    }
}
package18个程序_多线程.a6_线程交互.练习3_生产者_消费者;

import java.util.ArrayList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        List<Producer> producers = new ArrayList<>();
        List<Consumer> consumers = new ArrayList<>();
        MyStack stack = new MyStack();

        for (int i = 0; i < 2; i++){
            Producer producer = new Producer("producer " + (i+1));
            producer.stack = stack;
            producers.add(producer);
        }
        for(int i = 0; i < 5; i++){
            Consumer consumer = new Consumer("consumer " + (i+1));
            consumer.stack = stack;
            consumers.add(consumer);
        }

        for (Producer producer:producers){
            producer.start();
        }
        for (Consumer consumer:consumers){
            consumer.start();
        }
    }
}

7 线程池

线程大量启动、结束 导致系统的性能变卡,响应变慢
引入线程池解决大量线程问题
线程池像生产者消费者模式,消费对象是运行的任务

https://blog.csdn.net/weixin_40598838/article/details/112861491

简单程序

自创线程池:

package18个程序_多线程.a7_线程池.s1_简单程序;

import java.util.LinkedList;

public class MyThread {
    // 线程池大小
    int size;

    // 任务器
    LinkedList<Runnable> list = new LinkedList<>();

    //企图消费的线程
    public MyThread(){
        size = 10;
        synchronized (list){
            for (int i = 0; i < size; i++){
                new ConsumerThread("任务消费者进程 " + (i+1)).start();
            }
        }
    }

    public void add(Runnable r){
        synchronized (list){
            list.add(r);
            list.notify();
        }
    }

    class ConsumerThread extends Thread{
        public ConsumerThread(String name){
            super(name);
        }
        Runnable r;
        public void run(){
            System.out.println("启动 " + this.getName());
            while (true){
                synchronized (list){
                    while (list.isEmpty()){
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    r = list.removeLast();
                    list.notify();
                }
                System.out.println(this.getName() + " 获取到任务,开始执行");
                r.run();
            }
        }
    }
}
package18个程序_多线程.a7_线程池.s1_简单程序;

public class test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        for (int i = 0; i < 5; i++){
            Runnable r = new Runnable() {
                @Override
                //空
                public void run() { }
            };
            myThread.add(r);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试

任务执行时间都是 1 秒
隔 1 秒,向线程池中添加任务
然后间隔时间越来越短,执行任务的线程还没结束,新任务又来了
观察到线程池里 其他线程被唤醒来执行这些任务

package18个程序_多线程.a7_线程池.s2_测试线程池;

import java.util.LinkedList;

public class MyThread {
    // 线程池大小
    int size;
    // 任务器
    LinkedList<Runnable> list = new LinkedList<>();

    //企图消费的线程
    public MyThread(){
        size = 10;
        synchronized (list){
            for (int i = 0; i < size; i++){
                new ConsumerThread("任务消费者进程 " + (i+1)).start();
            }
        }
    }

    public void add(Runnable r){
        synchronized (list){
            list.add(r);
            list.notify();
        }
    }

    class ConsumerThread extends Thread{
        public ConsumerThread(String name){
            super(name);
        }
        Runnable r;
        public void run(){
            System.out.println("启动 " + this.getName());
            while (true){
                synchronized (list){
                    while (list.isEmpty()){
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    r = list.removeLast();
                    list.notify();
                }
                System.out.println(this.getName() + " 获取到任务,开始执行");
                r.run();
            }
        }
    }
}
package18个程序_多线程.a7_线程池.s2_测试线程池;

public class test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        int sleep = 1000;

        while (true){
            myThread.add(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            try {
                Thread.sleep(sleep);
                sleep = sleep>100?(sleep-100):sleep;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

java自带线程池

ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
第 1 个参数10:该线程池初始化了 10 个线程工作
第 2 个参数15:如果 10 个线程不够用,会自动增加到最多 15 个线程
第 3 个参数60:结合第 4 个参数 TimeUnit.SECONDS:经过 60 秒,多出的线程还没有接到活儿,就会回收,最后保持池子里就 10 个线程
第 5 个参数 new LinkedBlockingQueue():用来放任务的集合

execute方法用于添加新的任务

package18个程序_多线程.a7_线程池.s3_java自带线程池;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class test {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务1");
            }
        });
    }
}

练习

之前 查找文件内容 ,若文件特别多,就会创建很多线程。
改写:
用线程池来完成
初始化大小是 10 的线程池
遍历所有文件,当遍历到文件是 .java 时,创建一个查找文件的任务,把这个任务扔进线程池去执行,继续遍历下一个文件

package18个程序_多线程.a7_线程池.练习;

import java.io.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SearchFileThread {
    private File file;
    private String search;


    public SearchFileThread(){}
    public SearchFileThread(File file, String search){
        this.file = file;
        this.search = search;
    }

    private String readFile(File file){
        try (
            FileReader fileReader = new FileReader(file);
            ){
            char[] dates = new char[(int) file.length()];
            fileReader.read(dates);
            return new String(dates);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public void Search(File file, String search){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,15,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        File[] files = file.listFiles();
        for (File file1: files){
            if (file1.isDirectory()){
                Search(file1,search);
            }else if (file1.isFile() && file1.getName().endsWith(".java")){
                threadPoolExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        char[] dates = new char[(int) file1.length()];
                        try (
                            BufferedReader bufferedReader = new BufferedReader(new FileReader(file1));
                            ){
                            bufferedReader.read(dates);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (String.valueOf(dates).contains(search)){
                            System.out.println("找到字符串在 " + file1.getName() + " - " + file1.getAbsolutePath());
                        }
                    }
                });
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
package18个程序_多线程.a7_线程池.练习;

import java.io.File;

public class test {
    public static void main(String[] args) {
        String path = "src\\第18个程序_多线程\\a7_线程池\\练习\\src";
        File file = new File(path);
        if(!file.exists()){
            System.out.println("该文件夹不存在!");
            return;
        }
        SearchFileThread searchFileThread = new SearchFileThread();
        searchFileThread.Search(file,"File");
    }
}

8 Lock 对象(同步)

和 synchronize 效果一样
Lock lock = new ReentrantLock(); 当前线程占用 lock 对象,其他线程就不能占用了

区别:
synchronized 块结束,会自动释放 someObject 的占用
lock 必须用 unlock 方法手动释放,unlock 放在 finally 中进行

synchronized 的方式

9 原子访问

volatile

分布式锁

https://github.com/apache/shardingsphere/issues/5882

禁止安全检查
management:
  health:
    db:
      enabled: false

lock、synchronize 只在单 jvm 有效,多集群 会失效

3中实现方式:

数据库

redis:
redis 的key 唯一




CPU 告诉你硬盘和网络到底有多慢
https://cizixs.com/2017/01/03/how-slow-is-disk-and-network/

java多线程有什么作用 好处?
https://cloud.tencent.com/developer/article/1841361
作用:
1、java多线程能够将各个任务分开执行,分开后的任务会同步进行
无需等待更多时间,效率也会更高
比如下载文件时如果使用java多线程的话,就能够同时下载多个文件。

2、java多线程能够分段执行
假如一个线程出现网络卡死的情况,那么多线程不会一直等待网络恢复正常,而是先执行其他的访问,如果操作超时会自动报错并释放相应线程

https://pdai.tech/md/interview/x-interview.html#32-并发关键字

https://www.liaoxuefeng.com/wiki/1252599548343744/1306580710588449

synchronized:
https://blog.csdn.net/m0_53474063/article/details/112389756

Java 对象的内存布局
https://www.51cto.com/article/713308.html

volatile
https://blog.csdn.net/didi1663478999/article/details/98523122
https://blog.csdn.net/xueping_wu/article/details/124541419

Java线程池 Executor

public interface eService extends Executor {
}

xxx implements eService {
}{
	class a implements Runnable{
		@Override
        public void run(){
        ...
        }
	}

	eService service;
	void cc(){
		service.execute(); // 会执行 上面的run()方法
	}

}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1403034144

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值