多线程[狂神笔记]

多线程

Process与Thread

  • 程序:指令和数据的有限集合,其本身没有任何进行的含义,是一个静态的概念
  • 进程:执行程序的一次执行过程,是一个动态概念。是系统资源分配的单位
  • 线程:CPU调度和执行的单位

注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快。所以有同时执行的错觉

普通方法调用和多线程

  1. 主线程调用run()–>主线程执行run()

    只有主线程一条执行路径

  2. 主线程调用start()–>子线程执行run()

    多条执行路径,主线程和子线程并行交替执行

继承Thread

  • 继承Thread类
  • 重写run()方法
  • 调用start()开启线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

注意:线程开启不一定立即执行,由CPU调度执行

start()

public class ThreadText1 extends Thread{
    @Override
    public void run() {
        //run()方法线程体
        for (int i=0;i<200;i++){
            System.out.println("多线程"+i);
        }
    }

    public static void main(String[] args) {

        ThreadText1 threadText1=new ThreadText1();
        threadText1.start();

        for (int i=0;i<200;i++){
            System.out.println("学习多线程"+i);
        }
    }
}
/*部分结果
学习多线程132
学习多线程133
多线程113
多线程114
多线程115
学习多线程134
学习多线程135
*/

run()

public class ThreadText1 extends Thread{
    @Override
    public void run() {
        //run()方法线程体
        for (int i=0;i<200;i++){
            System.out.println("多线程"+i);
        }
    }

    public static void main(String[] args) {

        ThreadText1 threadText1=new ThreadText1();
        threadText1.run();

        for (int i=0;i<200;i++){
            System.out.println("学习多线程"+i);
        }
    }
}
/*部分结果
多线程197
多线程198
多线程199
学习多线程0
学习多线程1
学习多线程2
*/

实现Runnable

  • 定义MyRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
public class ThreadText3 implements Runnable{
    @Override
    public void run() {
        //run()方法线程体
        for (int i=0;i<200;i++){
            System.out.println("多线程"+i);
        }
    }

    public static void main(String[] args) {

        ThreadText3 threadText3=new ThreadText3();
//        Thread thread=new Thread(threadText3);
//        thread.start();
//        等同于
        new Thread(threadText3).start();

        for (int i=0;i<200;i++){
            System.out.println("学习多线程"+i);
        }
    }
}
/*
多线程134
多线程135
多线程136
学习多线程181
学习多线程182
*/

网图下载

继承Thread

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class ThreadText2 extends Thread{

    private String url;//图片url地址
    private String name;//下载后的文件名

    public ThreadText2(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);
    }

    public static void main(String[] args) {
        ThreadText2 t1=new ThreadText2("https://img.tukuppt.com/photo-small/00/34/09/5a4328226c563.jpg","1.jpg");
        ThreadText2 t2=new ThreadText2("https://img.tukuppt.com/photo-small/00/35/69/5a430a99d93af.jpg","2.jpg");
        ThreadText2 t3=new ThreadText2("https://img.tukuppt.com/photo-small/00/04/19/5a3c86f86fd1e.jpg","3.jpg");

        t1.start();//先下载t1
        t2.start();//然后t2
        t3.start();//最后t3
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));//将url下载到文件
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,download方法出现问题");
        }
    }
}
/*
下载的文件名为:3.jpg
下载的文件名为:1.jpg
下载的文件名为:2.jpg
下载顺序每回不同,说明线程同步执行
*/

实现Runnable接口

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class ThreadText2 implements Runnable{

    private String url;//图片url地址
    private String name;//下载后的文件名

    public ThreadText2(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);
    }

    public static void main(String[] args) {
        ThreadText2 t1=new ThreadText2("https://img.tukuppt.com/photo-small/00/34/09/5a4328226c563.jpg","1.jpg");
        ThreadText2 t2=new ThreadText2("https://img.tukuppt.com/photo-small/00/35/69/5a430a99d93af.jpg","2.jpg");
        ThreadText2 t3=new ThreadText2("https://img.tukuppt.com/photo-small/00/04/19/5a3c86f86fd1e.jpg","3.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));//将url下载到文件
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,download方法出现问题");
        }
    }
}
/*
下载的文件名为:3.jpg
下载的文件名为:1.jpg
下载的文件名为:2.jpg
*/

多个线程操作同一对象(初始并发问题)

买车票问题

//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class ThreadText4 implements Runnable{

    private int ticketNums=10;//票数

    @Override
    public void run() {
        while(true){
            if(ticketNums==0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }

    public static void main(String[] args) {
        ThreadText4 threadText4=new ThreadText4();

        new Thread(threadText4,"买家1").start();
        new Thread(threadText4,"买家2").start();
        new Thread(threadText4,"买家3").start();
    }
}
/*有重复票数
买家1拿到了第10张票
买家2拿到了第9张票
买家3拿到了第9张票
买家3拿到了第8张票
买家1拿到了第7张票
买家2拿到了第6张票
买家1拿到了第4张票
买家2拿到了第3张票
买家3拿到了第5张票
买家2拿到了第2张票
买家3拿到了第2张票
买家1拿到了第1张票
*/

案例—龟兔赛跑

要求:

  1. 首先设置赛道距离,然后要求离终点越来越近
  2. 判断比赛是否结束
  3. 打印出胜利者
  4. 龟兔赛跑开始
  5. 故事中兔子需要睡觉,需要模拟兔子睡觉
  6. 最后乌龟赢得比赛
public class Race implements Runnable{

    private static String winner;

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            boolean flag=gameOver(i);
            if (flag){
                break;
            }

            if (Thread.currentThread().getName().equals("rabbit")&&i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
        }
    }
    private boolean gameOver(int meter){
        if (winner!=null){
            return true;
        }{
            if (meter==100){
                winner=Thread.currentThread().getName();
                System.out.println("winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race=new Race();

        new Thread(race,"rabbit").start();
        new Thread(race,"turtle").start();
    }
}
/*
rabbit跑了1米
turtle跑了1米
rabbit跑了2米
turtle跑了2米
rabbit跑了3米
turtle跑了3米
rabbit跑了4米
turtle跑了4米
rabbit跑了5米
turtle跑了5米
rabbit跑了6米
rabbit跑了7米
turtle跑了6米
rabbit跑了8米
turtle跑了7米
rabbit跑了9米
turtle跑了8米
turtle跑了9米
turtle跑了10米
turtle跑了11米
turtle跑了12米
turtle跑了13米
turtle跑了14米
turtle跑了15米
turtle跑了16米
turtle跑了17米
turtle跑了18米
turtle跑了19米
turtle跑了20米
turtle跑了21米
turtle跑了22米
turtle跑了23米
turtle跑了24米
turtle跑了25米
turtle跑了26米
turtle跑了27米
turtle跑了28米
turtle跑了29米
turtle跑了30米
turtle跑了31米
turtle跑了32米
turtle跑了33米
turtle跑了34米
turtle跑了35米
turtle跑了36米
turtle跑了37米
turtle跑了38米
turtle跑了39米
turtle跑了40米
turtle跑了41米
turtle跑了42米
turtle跑了43米
turtle跑了44米
turtle跑了45米
turtle跑了46米
turtle跑了47米
turtle跑了48米
turtle跑了49米
turtle跑了50米
turtle跑了51米
turtle跑了52米
turtle跑了53米
turtle跑了54米
turtle跑了55米
turtle跑了56米
turtle跑了57米
turtle跑了58米
turtle跑了59米
turtle跑了60米
turtle跑了61米
turtle跑了62米
turtle跑了63米
turtle跑了64米
turtle跑了65米
turtle跑了66米
turtle跑了67米
turtle跑了68米
turtle跑了69米
turtle跑了70米
turtle跑了71米
turtle跑了72米
turtle跑了73米
turtle跑了74米
turtle跑了75米
turtle跑了76米
turtle跑了77米
turtle跑了78米
turtle跑了79米
turtle跑了80米
turtle跑了81米
turtle跑了82米
turtle跑了83米
turtle跑了84米
turtle跑了85米
turtle跑了86米
turtle跑了87米
turtle跑了88米
turtle跑了89米
turtle跑了90米
turtle跑了91米
turtle跑了92米
turtle跑了93米
turtle跑了94米
turtle跑了95米
turtle跑了96米
turtle跑了97米
turtle跑了98米
turtle跑了99米
winner is turtle
rabbit跑了10米
*/

实现Callable接口

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser=Executors.newFixedThreadPool()
  5. 提交执行:Future result=ser.submit(t1)
  6. 获取结果:boolean r1=result.get()
  7. 关闭服务:ser.shutdownNow();

好处:

  1. 可以创建返回值
  2. 可以抛出异常
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class TextCallable implements Callable<Boolean> {
    private String url;//图片url地址
    private String name;//下载后的文件名

    public TextCallable(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call() {
        WebDownloader1 webDownloader=new WebDownloader1();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TextCallable t1=new TextCallable("https://img.tukuppt.com/photo-small/00/34/09/5a4328226c563.jpg","1.jpg");
        TextCallable t2=new TextCallable("https://img.tukuppt.com/photo-small/00/35/69/5a430a99d93af.jpg","2.jpg");
        TextCallable t3=new TextCallable("https://img.tukuppt.com/photo-small/00/04/19/5a3c86f86fd1e.jpg","3.jpg");

        //创建执行服务    线程数量:3
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1= ser.submit(t1);
        Future<Boolean> r2= ser.submit(t2);
        Future<Boolean> r3= ser.submit(t3);
        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        //关闭服务
        ser.shutdownNow();
    }
}

//下载器
class WebDownloader1{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));//将url下载到文件
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,download方法出现问题");
        }
    }
}
/*
下载的文件名为:1.jpg
下载的文件名为:2.jpg
下载的文件名为:3.jpg
true
true
true
*/

静态代理

静态代理模式总结:

  1. 真实对象和代理对象都要实现同一个接口
  2. 代理对象要代理真实角色

(Runnable,Thread 使用静态代理模式)

好处:

  1. 代理对象可以做很多真实对象做不了的事情
  2. 真实对象专注做自己的事情
public class TextMarry {
 public static void main(String[] args) {
  You you=new You();
  WedingCompany wedingCompany=new WedingCompany(you);
  wedingCompany.getMarry();
 }
}

interface Marry{
 void getMarry();
}

class You implements Marry{
 @Override
 public void getMarry() {
  System.out.println("X要结婚了");
 }
}

class WedingCompany implements Marry{
 private Marry target;

 public WedingCompany(Marry target){
  this.target=target;
 }

 @Override
 public void getMarry() {
  before();
  this.target.getMarry();
  after();
 }
 private void before(){
  System.out.println("结婚前,布置现场");
 }
 private void after(){
  System.out.println("结婚后,收取尾款");
 }
}
/*
结婚前,布置现场
X要结婚了
结婚后,收取尾款
*/

Lambda表达式

  • λ 希腊字母表中排序第十一位的字母,英语名称为Lambda
  • 避免匿名内部类定义过多
  • 其实质属于函数式编程的概念
(params)->expression[表达式]
(params)->ststement[语句]
(params)->{statements}
a->System.out.println("i like lambda-->"+a);
new Thread(()->System.out.println("多线程学习")).start();

好处:

  1. 避免匿名内部类定义过多
  2. 可以让你的代码看起来很简洁
  3. 去掉了一堆没有意义的代码,只留下核心的逻辑

Lambda表达式

  • 理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在

  • 函数式接口的定义:

    • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
    public interface Runnable{
        public adstact void run();
    }
    
    • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
public class TextLambda {
    //静态内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }
    }

    public static void main(String[] args) {
        ILike like=new Like();
        like.lambda();

        like=new Like2();
        like.lambda();

        //局部内部类
        class Like3 implements ILike {
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        like=new Like3();
        like.lambda();

        //匿名内部类,没有类的名称,必须借助接口或者父类
        like=new ILike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();

        //用lambda简化
        like=()-> {
            System.out.println("i like lambda5");
        };
        like.lambda();
    }
}
//定义一个函数式接口
interface ILike{
    void lambda();
}
//实现类
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("i like lambda1");
    }
}
/*
i like lambda1
i like lambda2
i like lambda3
i like lambda4
i like lambda5
*/
方法一(实现类)
public class TextLambda2 {
    public static void main(String[] args) {
        ILove love=new Love();
        love.love(1);
    }
}

interface ILove{
    void love(int a);
}

class Love implements ILove{
    @Override
    public void love(int a) {
        System.out.println("i love -->"+a);
    }
}
/*
i love -->1
*/
方法二(静态内部类)
public class TextLambda2 {
    static class Love implements ILove{
        @Override
        public void love(int a) {
            System.out.println("i love -->"+a);
        }
    }
    public static void main(String[] args) {
        ILove love=new Love();
        love.love(2);
    }
}

interface ILove{
    void love(int a);
}
/*
i love -->2
*/
方法三(成员内部类)
public class TextLambda2 {

    public static void main(String[] args) {
        class Love implements ILove{
            @Override
            public void love(int a) {
                System.out.println("i love -->"+a);
            }
        }

        ILove love=new Love();
        love.love(3);
    }
}

interface ILove{
    void love(int a);
}
/*
i love -->3
*/
方法四(匿名内部类)
public class TextLambda2 {

    public static void main(String[] args) {
        ILove love=new ILove(){
            @Override
            public void love(int a) {
                System.out.println("i love-->"+a);
            }
        };
        love.love(4);
    }
}

interface ILove{
    void love(int a);
}
/*
i love-->4
*/
方法五(使用lambda简化)
public class TextLambda2 {

    public static void main(String[] args) {
        ILove love=(int a)->{
            System.out.println("i love-->"+a);
        };
        love.love(5);
    }
}

interface ILove{
    void love(int a);
}
/*
i love-->5
*/
//简化1:参数类型(多个函数也可以去掉参数类型,要去都去)
        ILove love=(a)->{
            System.out.println("i love-->"+a);
        };
 //简化2:小括号(只有一个参数时)
        ILove love=a->{
            System.out.println("i love-->"+a);
        };
//简化3:花括号(只有一个语句时)
        ILove love=a-> System.out.println("i love-->"+a);

线程状态

五大状态:

  • 创建状态—(启动线程)—>就绪状态
  • 就绪状态—(获得CPU资源)—>运行状态
  • 运行状态—(释放CPU资源)—>就绪状态
  • 阻塞状态—(阻塞解除)—>就绪状态
  • 运行状态—(等待用户输入线程休眠等)—>阻塞状态
  • 运行状态—(线程自然执行完毕,外部干涉终止线程)—>死亡状态
常用方法
  • setPriority(int newPriority)——更改线程的优先级
  • static void sleep(long millis) ——在指定的毫秒数内让当前正在执行的线程休眠
  • void join()——等待该线程终止
  • static void yield()——暂停当前正在执行的线程对象,并执行其它线程
  • void interrupt()——中断线程,别用这个方法
  • boolean isAlive()——测试线程是否处于活动状态
停止线程
  • 不推荐使用JDK提供的stop()、destory()方法(已废弃)
  • 推荐线程自己停止下来—>利用次数,不建议死循环
  • 建议使用一个标志位进行终止变量,当flag=false,则终止线程运行
public class TextStop implements Runnable{
    //设置一个标识位
    private boolean flag=true;

    @Override
    public void run() {
        int i=1;
        while(flag){
            System.out.println("子线程"+i++);
        }
    }
    //设置一个公开的方法停止线程,转换标志位
    public void stop(){
        flag=false;
    }

    public static void main(String[] args) {
        TextStop textStop=new TextStop();
        new Thread(textStop).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
            if (i==50){
                //调用stop方法切换标志位,停止线程
                textStop.stop();
                System.out.println("子线程该停止了");
            }
        }
    }
}
/*
main0
子线程1
子线程2
子线程3
main1
子线程4
子线程5
子线程6
main2
子线程7
子线程8
子线程9
子线程10
子线程11
子线程12
子线程13
子线程14
子线程15
子线程16
子线程17
子线程18
子线程19
子线程20
子线程21
子线程22
main3
子线程23
子线程24
main4
子线程25
子线程26
main5
main6
main7
main8
main9
main10
main11
main12
main13
main14
main15
main16
main17
main18
子线程27
子线程28
子线程29
子线程30
子线程31
main19
子线程32
子线程33
子线程34
main20
main21
子线程35
子线程36
main22
main23
main24
main25
main26
main27
子线程37
子线程38
子线程39
子线程40
main28
main29
main30
main31
main32
main33
main34
main35
main36
main37
main38
main39
子线程41
main40
子线程42
main41
子线程43
main42
main43
main44
main45
main46
main47
main48
main49
main50
子线程44
子线程该停止了
main51
main52
main53
main54
main55
main56
main57
main58
main59
main60
main61
main62
main63
main64
main65
main66
main67
main68
main69
main70
main71
main72
main73
main74
main75
main76
main77
main78
main79
main80
main81
main82
main83
main84
main85
main86
main87
main88
main89
main90
main91
main92
main93
main94
main95
main96
main97
main98
main99
*/
线程休眠
  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁
//模拟网络延时:放大问题的发生性
public class ThreadText4 implements Runnable{
    private int ticketNums=10;//票数

    @Override
    public void run() {
        while(true){
            if(ticketNums==0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }

    public static void main(String[] args) {
        ThreadText4 threadText4=new ThreadText4();

        new Thread(threadText4,"买家1").start();
        new Thread(threadText4,"买家2").start();
        new Thread(threadText4,"买家3").start();
    }
}

倒计时

import java.text.SimpleDateFormat;
import java.util.Date;

//模拟倒计时
public class TextSleep {

    public static void main(String[] args) throws InterruptedException {
        //打印系统时间
        Date startTime=new Date(System.currentTimeMillis());
        while(true){
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
            startTime=new Date(System.currentTimeMillis());//更新当前时间
        }
    }

    public static void tenDown() throws InterruptedException {
        int i=10;
        while(true){
            Thread.sleep(1000);
            System.out.println(i--);
            if(i<=0){
                break;
            }
        }
    }
}
/*
19:40:53
19:40:54
19:40:55
19:40:56
19:40:57
19:40:58
19:40:59
19:41:00
*/
线程礼让
  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功!看CPU心情
public class TextYield {
    public static void main(String[] args) {
        MyYield myYield=new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}
/*
a线程开始执行
b线程开始执行
b线程停止执行
a线程停止执行
*/
合并线程
  • Join合并线程,代此线程执行完成后,再执行其它线程,其它线程阻塞
  • 可以想象成插队
public class TextJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("join"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TextJoin textJoin=new TextJoin();
        Thread thread=new Thread(textJoin);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if(i==200){
                thread.join();//main线程阻塞
            }
        }
    }
}
/*
......
main197
main198
main199
main200
join203
join204
join205
......
join997
join998
join999
main201
main202
main203
*/
线程状态观测

线程状态。线程可以处于以下状态之一:

  • Thread.State

    • NEW——尚未启动的线程处于此状态
    • RUNNABLE——在Java虚拟机中执行的线程处于此状态
    • BLOCKED——被阻塞等待监视器锁定的线程处于此状态
    • WAITING——正在等待另一个线程执行特定动作的线程处于此状态
    • TIMED_WATING——正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
    • TERMINATED——已退出的线程处于此状态(一旦进入死亡状态,就不能再次启动)

    一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态

public class TextState{
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("===============");
        });

        //观察状态
        Thread.State state=thread.getState();
        System.out.println(state);//输出状态

        thread.start();
        state=thread.getState();
        System.out.println(state);

        while (state!=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
            Thread.sleep(500);
            state=thread.getState();
            System.out.println(state);
        }

    }
}
/*
NEW
RUNNABLE
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
===============
TERMINATED
*/
线程优先级
  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
  • 线程的优先级用数字表示,范围从1~10
    • Thread.MIN_PRIORITY=1;
    • Thread.MAX_PRIORITY=10;
    • Thread.NORM_PRIORITY=5;
  • 使用以下方式改变或获取优先级
    • getPriority().setPriority(int xxx)
public class TextPriority{
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority=new MyPriority();

        Thread t1=new Thread(myPriority,"1");
        Thread t2=new Thread(myPriority,"2");
        Thread t3=new Thread(myPriority,"3");
        Thread t4=new Thread(myPriority,"4");
        Thread t5=new Thread(myPriority,"5");
        Thread t6=new Thread(myPriority,"6");

        //先设置优先级,再启动
        t1.start();
        t2.setPriority(1);
        t2.start();
        t3.setPriority(4);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
        t5.setPriority(8);
        t5.start();
        t6.setPriority(7);
        t6.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}
/*
main-->5
1-->5
4-->10
5-->8
3-->4
2-->1
6-->7
*/

优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度

守护(deamon)线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如:后台记录操作日志,监控内存,垃圾回收等待
//测试守护线程
//上帝守护你
public class TextDeamon {
    public static void main(String[] args) {
        You2 you2=new You2();
        God god=new God();

        Thread thread1=new Thread(you2);
        Thread thread2=new Thread(god);
        thread2.setDaemon(true);//默认为false,用户线程

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

class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("上帝守护你");
        }

    }
}

class You2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一生都快乐的活着");
        }
        System.out.println("Goodby,world!");
    }
}
/*
上帝守护你
上帝守护你
Goodby,world!
上帝守护你
上帝守护你
上帝守护你
上帝守护你
上帝守护你
*/

线程同步(多个线程操作同一个资源)

  • 并发:同一个对象被多个线程同时操作
  • 线程同步其实是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
  • 形成条件:队列+锁
  • 存在访问冲突问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起
    • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
三大不安全案例
不安全买票
//线程不安全,有重复票数
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();

        new Thread(buyTicket,"A").start();
        new Thread(buyTicket,"B").start();
        new Thread(buyTicket,"C").start();
    }
}
class BuyTicket implements Runnable{

    int ticketNum=10;
    boolean flag=true;//外部停止方式

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNum<=0){
            flag=false;
            return;
        }
        //模拟延时
        Thread.sleep(1000);
        //买票
        System.out.println(Thread.currentThread().getName()+"买到第"+ticketNum--+"张票");
    }
}
/*
A买到第10张票
C买到第9张票
B买到第8张票
C买到第7张票
B买到第7张票
A买到第7张票
B买到第5张票
C买到第4张票
A买到第6张票
A买到第3张票
B买到第1张票
C买到第2张票
*/
不安全取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account=new Account(100,"基金");

        Drawing p1=new Drawing(account,50,"p1");
        Drawing p2=new Drawing(account,100,"p2");

        p1.start();
        p2.start();
    }
}
//账户
class Account{
    int money;
    String name;

    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }
}
//银行:模拟取款
class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//现在手里有多少钱

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }

    @Override
    public void run() {
        //判断有没有钱
        if(account.money-drawingMoney<0){
            System.out.println("余额不足");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //卡内余额=余额-你取的钱
        account.money=account.money-drawingMoney;
        //你手里的钱
        nowMoney=nowMoney+drawingMoney;

        System.out.println(account.name+"余额为:"+account.money);
        //Thread.currentThread().getName()=this.getName();
        System.out.println(this.getName()+"手里的钱有"+nowMoney);
    }
}
/*
基金余额为:-50
基金余额为:-50
p2手里的钱有100
p1手里的钱有50
*/
线程不安全的集合
import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        for (int i=0;i<10000;i++){
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
/*
9999
*/

同步方法

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,即synchronized关键字,它包括两种用法:
    • synchronized方法
    • synchronized块
  • 同步方法:public synchronized void method(int args){}
  • synchronized方法控制对”对象“的访问,每个对象对应一把锁,每个对象都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才能释放锁,后面被阻挠的线程才能获得这个锁,继续执行
  • 缺陷:若将一个大的方法声明为synchronized将会影响效率
  • 方法里面需要修改的内容才需要锁,锁的太多,浪费资源
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();

        new Thread(buyTicket,"A").start();
        new Thread(buyTicket,"B").start();
        new Thread(buyTicket,"C").start();
    }
}
class BuyTicket implements Runnable{

    int ticketNum=10;
    boolean flag=true;

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //synchronized同步方法,锁的是this
    public synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNum<=0){
            flag=false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"买到第"+ticketNum--+"张票");
    }
}
/*
A买到第10张票
A买到第9张票
A买到第8张票
A买到第7张票
A买到第6张票
A买到第5张票
A买到第4张票
A买到第3张票
C买到第2张票
C买到第1张票
*/
同步块
  • 同步块:synchronized(Obj){}
  • Obj称之为同步监视器
    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class(反射中讲解)
  • 同步监视器的执行过程
    • 第一个线程访问,锁定同步监视器,执行其中代码
    • 第二个线程访问,发现同步监视器被锁定,无法访问
    • 第一个线程访问完毕,解锁同步监视器
    • 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
public class UnsafeBank {
    public static void main(String[] args) {
        Account account=new Account(100,"基金");

        Drawing p1=new Drawing(account,50,"p1");
        Drawing p2=new Drawing(account,100,"p2");

        p1.start();
        p2.start();
    }
}
//账户
class Account{
    int money;
    String name;

    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }
}
//银行:模拟取款
class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//现在手里有多少钱

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }

    @Override
    public void run() {
        //锁的对象就是变化的量,需要增删改的对象
        synchronized(account){
            //判断有没有钱
            if(account.money-drawingMoney<0){
                System.out.println("余额不足");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内余额=余额-你取的钱
            account.money=account.money-drawingMoney;
            //你手里的钱
            nowMoney=nowMoney+drawingMoney;

            System.out.println(account.name+"余额为:"+account.money);
            //Thread.currentThread().getName()=this.getName();
            System.out.println(this.getName()+"手里的钱有"+nowMoney);
        }
    }
}
/*
基金余额为:50
p1手里的钱有50
余额不足
*/
import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        for (int i=0;i<10000;i++){
            new Thread(()->{
                synchronized(list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
/*
10000
*/
CopyOnWriteArrayList
//测试JUC安全类型的集合
public class TextJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                    list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}
/*
10000
*/

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有”两个以上对象的锁“时,就可能会发生”死锁的问题“

public class DeadLock {
    public static void main(String[] args) {
        MakeUp A=new MakeUp(0,"A");
        MakeUp B=new MakeUp(1,"B");

        A.start();
        B.start();
    }
}
class LipStick{}

class Mirror{}

class MakeUp extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    static LipStick lipStick=new LipStick();
    static Mirror mirror=new Mirror();

    int choice;//选择
    String name;//化妆的人的名字

    MakeUp(int choice,String name){
        this.choice=choice;
        this.name=name;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void makeup() throws InterruptedException {
        if(choice==0){
            synchronized(lipStick){
                System.out.println(this.name+"获得了口红");
                Thread.sleep(1000);
            }
            synchronized (mirror){
                System.out.println(this.name+"获得了镜子");
            }
        }else{
            synchronized(mirror){
                System.out.println(this.name+"获得了镜子");
                Thread.sleep(2000);
            }
            synchronized (lipStick){
                System.out.println(this.name+"获得了口红");
            }
        }
    }
}
/*
B获得了镜子
A获得了口红
B获得了口红
A获得了镜子
*/

死锁避免方法

  • 产生死锁的四个条件:
  1. 互斥条件:一个资源一次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

只要破其中一个或多个条件就可以避免死锁发生

Lock(锁)

  • 从JDK5.0开始,Java提供了更强大的线程同步机制—通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前因先获得Lock对象
  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReetrantLock,可以显式加锁,释放锁
public class TextLock {
    public static void main(String[] args) {
        TextLock2 t1=new TextLock2();

        new Thread(t1).start();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
class TextLock2 implements Runnable{
    int ticketNume=10;
    @Override
    public void run() {
        while(true){
            if (ticketNume>0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ticketNume--);
            }else {
                break;
            }
        }
    }
}
/*
9
8
10
7
5
6
4
4
3
2
1
0
*/
import java.util.concurrent.locks.ReentrantLock;

public class TextLock {
    public static void main(String[] args) {
        TextLock2 t1=new TextLock2();

        new Thread(t1).start();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
class TextLock2 implements Runnable{
    int ticketNume=10;
    private final ReentrantLock lock=new ReentrantLock();//定义lock锁
    @Override
    public void run() {
        while(true){
            try{
                lock.lock();//加锁
                if (ticketNume>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNume--);
                }else {
                    break;
                }
            }finally{
                lock.unlock();
            }

        }
    }
}
/*
10
9
8
7
6
5
4
3
2
1
*/
synchronized和Lock的对比
  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:
    • Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

线程协作(生产者消费者问题)

  • 应用场景:生产者消费者问题
    • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
    • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
    • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
线程通信-分析

这是一个线程同步问题,生产者和消费者共享一个资源,并且生产者和消费者之间相互依赖,互为条件

  • 对于生产者,没有生产产品之前,需要通知消费者等待。而生产了产品之后,又要马上通知消费者消费
  • 对于消费者,在消费之后,需通知生产者已经结束消费,需要生产新的产品以供消费
  • 在生产者消费者问题中,仅有synchronized是不够的
    • synchronized可阻止并发更新同一个共享资源,实现了同步
    • synchronized不能用来实现不同线程之间的消息传递(通信)
  • Java提供的方法
    • wait—表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
    • wait(long timeout)—指定等待的毫秒数
    • notify()—唤醒一个处于等待状态的线程
    • notifyAll()—唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
    • 注意:均是Object类方法,都只能在同步方法或者同步方法块中使用,否则会抛出异常lllegalMonitorStateException

解决方法1(并发协作模型“生产者/消费者模式”–>管程法)

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”
  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
public class TextPC {
    public static void main(String[] args) {
        synContainer container=new synContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends Thread{
    synContainer container;

    public Productor(synContainer container){
        this.container=container;
    }
    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}
//消费者
class Consumer extends Thread{
    synContainer container;

    public Consumer(synContainer container){
        this.container=container;
    }
    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了第"+container.pop().id+"只鸡");
        }
    }
}
//产品
class Chicken{
    int id;
    public Chicken(int id){
        this.id=id;
    }
}
//缓冲区
class synContainer{
    //需要一个容器大小
    Chicken[] chickens=new Chicken[10];
    //容器技术计数器
    int count=0;

    //生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了
        if (count==chickens.length){
            //通知消费者消费,生产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,放入产品
        chickens[count]=chicken;
        count++;
        //可以通知消费者消费了
        this.notify();
    }
    //消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if(count==0){
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken=chickens[count];
        //消费完了,通知生产者生产
        this.notifyAll();
        
        return chicken;
    }
}
/*
生产了0只鸡
生产了1只鸡
生产了2只鸡
生产了3只鸡
生产了4只鸡
生产了5只鸡
生产了6只鸡
生产了7只鸡
生产了8只鸡
消费了第8只鸡
消费了第9只鸡
消费了第7只鸡
消费了第6只鸡
消费了第5只鸡
消费了第4只鸡
消费了第3只鸡
消费了第2只鸡
消费了第1只鸡
消费了第0只鸡
生产了9只鸡
生产了10只鸡
消费了第10只鸡
生产了11只鸡
消费了第11只鸡
生产了12只鸡
消费了第12只鸡
生产了13只鸡
消费了第13只鸡
生产了14只鸡
消费了第14只鸡
生产了15只鸡
消费了第15只鸡
生产了16只鸡
生产了17只鸡
生产了18只鸡
生产了19只鸡
生产了20只鸡
生产了21只鸡
消费了第16只鸡
生产了22只鸡
消费了第22只鸡
消费了第23只鸡
消费了第21只鸡
消费了第20只鸡
消费了第19只鸡
消费了第18只鸡
消费了第17只鸡
生产了23只鸡
生产了24只鸡
消费了第24只鸡
生产了25只鸡
消费了第25只鸡
生产了26只鸡
消费了第26只鸡
生产了27只鸡
消费了第27只鸡
生产了28只鸡
消费了第28只鸡
生产了29只鸡
消费了第29只鸡
生产了30只鸡
消费了第30只鸡
生产了31只鸡
消费了第31只鸡
生产了32只鸡
消费了第32只鸡
生产了33只鸡
消费了第33只鸡
生产了34只鸡
消费了第34只鸡
生产了35只鸡
消费了第35只鸡
消费了第36只鸡
生产了36只鸡
生产了37只鸡
生产了38只鸡
消费了第37只鸡
生产了39只鸡
消费了第39只鸡
生产了40只鸡
消费了第40只鸡
生产了41只鸡
生产了42只鸡
消费了第41只鸡
生产了43只鸡
消费了第43只鸡
生产了44只鸡
生产了45只鸡
生产了46只鸡
生产了47只鸡
消费了第44只鸡
生产了48只鸡
消费了第48只鸡
生产了49只鸡
消费了第49只鸡
生产了50只鸡
生产了51只鸡
生产了52只鸡
消费了第50只鸡
消费了第53只鸡
消费了第52只鸡
消费了第51只鸡
消费了第47只鸡
消费了第46只鸡
消费了第45只鸡
消费了第42只鸡
消费了第38只鸡
生产了53只鸡
生产了54只鸡
生产了55只鸡
生产了56只鸡
生产了57只鸡
生产了58只鸡
生产了59只鸡
生产了60只鸡
生产了61只鸡
生产了62只鸡
生产了63只鸡
消费了第62只鸡
生产了64只鸡
消费了第64只鸡
生产了65只鸡
生产了66只鸡
消费了第65只鸡
消费了第66只鸡
生产了67只鸡
生产了68只鸡
消费了第67只鸡
消费了第68只鸡
生产了69只鸡
消费了第69只鸡
生产了70只鸡
消费了第70只鸡
生产了71只鸡
消费了第71只鸡
生产了72只鸡
消费了第72只鸡
生产了73只鸡
消费了第73只鸡
生产了74只鸡
消费了第74只鸡
生产了75只鸡
消费了第75只鸡
生产了76只鸡
消费了第76只鸡
生产了77只鸡
消费了第77只鸡
生产了78只鸡
消费了第78只鸡
生产了79只鸡
消费了第79只鸡
生产了80只鸡
消费了第80只鸡
生产了81只鸡
消费了第81只鸡
生产了82只鸡
消费了第82只鸡
生产了83只鸡
消费了第83只鸡
生产了84只鸡
消费了第84只鸡
生产了85只鸡
消费了第85只鸡
生产了86只鸡
消费了第86只鸡
生产了87只鸡
消费了第87只鸡
生产了88只鸡
消费了第88只鸡
生产了89只鸡
消费了第89只鸡
生产了90只鸡
消费了第90只鸡
生产了91只鸡
消费了第91只鸡
生产了92只鸡
消费了第92只鸡
生产了93只鸡
消费了第93只鸡
生产了94只鸡
消费了第94只鸡
生产了95只鸡
消费了第95只鸡
消费了第96只鸡
生产了96只鸡
消费了第63只鸡
生产了97只鸡
消费了第97只鸡
生产了98只鸡
消费了第98只鸡
生产了99只鸡
消费了第99只鸡
消费了第61只鸡
消费了第60只鸡
消费了第59只鸡
消费了第58只鸡
消费了第57只鸡
消费了第56只鸡
消费了第55只鸡
消费了第54只鸡
*/

解决方法2(并发协作模型“生产者/消费者模式”—>信号灯法)

public class TextPC2 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者-->演员
class Player extends Thread{
    TV tv=new TV();
    public Player(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("A no Arashi");
            }else{
                this.tv.play("VS Arashi");
            }
        }
    }
}
//消费者-->观众
class Watcher extends Thread{
    TV tv=new TV();
    public Watcher(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
//产品-->节目
class TV{
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String voice;//表演的节目
    boolean flag=true;
    //表演
public synchronized void play(String voice){
    if(!flag){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("演员表演了"+voice);
    //通知观众观看
    this.notifyAll();
    this.voice=voice;
    this.flag=!flag;
}
    //观看
    public synchronized void watch(){
    if(flag){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
        System.out.println("观看了"+voice);
    //通知演员表演
        this.notifyAll();
        this.flag=!flag;
    }
}
/*
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
演员表演了A no Arashi
观看了A no Arashi
演员表演了VS Arashi
观看了VS Arashi
*/

线程池

  • 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具
  • 好处
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
    • 便于线程管理(…)
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时间后会终止
  • JDK5.0起提供了线程池相关API:ExecutorService和Executors
  • EcecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TextPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //newFixThreadPool 参数为:线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2.关闭链接
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
/*
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值