线程Thread

Thread

内部类:

static class  Thread.State  //线程生命周期

每个线程都有优先级别:

static int MAX_PRIORITY    10
线程可以拥有的最大优先级。  
static int MIN_PRIORITY    1
线程可以拥有的最小优先级。  
static int NORM_PRIORITY   5
分配给线程的默认优先级。

理论上级别越大,先被执行的概率越大,具体得看cpu

常用构造:

Thread(Runnable target)  //分配一个新的 Thread对象。
Thread(Runnable target, String name)   //每个线程都有名字
Thread(String name)

常用方法:

static Thread currentThread() //返回对当前正在执行的线程对象的引用 

long getId() //返回此线程的标识符,自动生成,从1开始
String getName() //返回此线程的名称。  
int getPriority() //返回此线程的优先级。 
Thread.State getState() //获得线程的状态

void setName(String name) //将此线程的名称更改为等于参数 name 。  
void setPriority(int newPriority) //更改此线程的优先级。 

void join()  //等待这个线程死亡 
void join(long millis) //在指定时间millis内优先执行当前线程
void run()  //当前线程的运行逻辑都在run里面(禁止调用)
static void sleep(long millis) //使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)超时继续执行下一个功能
void start() //导致此线程开始执行; Java虚拟机调用此线程的run方法 
static void yield() //礼让,对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器  
    
//下面方法必须在同步中使用,并且需要监视器Monitor    
void wait() //当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 
void wait(long timeout) //当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过    
void notify() //唤醒正在wait对象监视器的单个线程。  
void notifyAll() //唤醒正在等待对象监视器的所有线程 

设置线程休眠的时候一般都是用TimeUnit枚举来进行设置:

public static void main(String[] args) {
    Thread thread = Thread.currentThread();
    try {
        Thread.sleep(1000);//不推荐这种方式,一般都用下面的枚举来进行设置休眠
        TimeUnit.SECONDS.sleep(12);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    thread.setName("main1");
    System.out.println(thread);
    System.out.println(thread.getPriority());
    System.out.println(thread.getId());
    System.out.println(thread.getState());
}

实现多线程的四种方式:

  1. 继承Thread 重写里面的run方法
public class ChildThread1 extends Thread {
    @Override
    public void run() {
        //重写的方法体
    }
}
  1. 实现Runnable接口,重写里面的run方法
public class ChildThread2 implements Runnable {
    @Override
    public void run() {
        //重写的方法体
    }
}

上面两种方法创建的线程的区别:

public class MainThread{
    //下面我们利用sleep来对线程进行交替开始
    public static void main(String[] args) {
    //该类实现了runnable接口,跟thread相当于同级,因此需要给他放到thread构造中将他变为thread,这里底层使用了静态代理设计模式
        ChildThread1 childThread1 = new ChildThread1();
        new Thread(childThread1,"小红").start();
    //直接继承thread接口里面会有start方法
        new ChildThread2("小明").start();
    }
}
//idea里面绿色启动按钮按了之后相当于启动了一个进程,会立即开启main,main线程里面开启子线程xx的时候不会阻塞,会继续执行,且子线程和主线程是交替执行。

run和start的区别:

直接调用run方法,就会走完当前线程的run方法之后执行下一个线程的run方法,此时不是多线程,start会开启多线程

public synchronized void start(){
    xxx;
    start0();
    //实现多线程的真正方法
    xxx;
}

问题来了,如何在main中控制他的子线程呢?

public class Demo2 {
    public static void main(String[] args) {
        ChildThread1 childThread1 = new ChildThread1();
        childThread1.start();
        try {
            //设置一秒后停止改线程
            Thread.sleep(1000);
            childThread1.setFlag(false);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ChildThread1 extends Thread {
    //为了让结果清晰一点,这个线程中设置一个变量并且输出
    private Integer count=0;
    //创建一个外部可以设置的控制变量
    private boolean flag=true;
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    @Override
    public void run() {
        while (flag){
            System.out.println("count:"+count++);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

join方法:插队,插入某线程等到他死亡/指定多少秒再执行当前线程

public class Demo2 {
    public static void main(String[] args) {
        ChildThread1 childThread1 = new ChildThread1();
        childThread1.start();
        for (int i = 0; i < 100; i++) {
            if(i==20) {
                try {
                    childThread1.join();
                    //等待childThread1线程走完,再走当前线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i);
        }
    }
}
class ChildThread1 extends Thread {
    private Integer count=0;
    @Override
    public void run() {
        String name=Thread.currentThread().getName();
        while (count<30){
            System.out.println(name+"-->count:"+count++);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

守护线程:

public class Demo2 {
    public static void main(String[] args) {
        ChildThread1 childThread1 = new ChildThread1();
        //将该线程设置为当前线程的守护线程,当前线程死亡,守护线程也随之结束
        childThread1.setDaemon(true);
        childThread1.start();
        String name=Thread.currentThread().getName();
        for (int i = 0; i < 10; i++) {
            System.out.println(name+":"+i);
        }
    }
}
class ChildThread1 extends Thread {
    private Integer count=0;
    @Override
    public void run() {
        String name=Thread.currentThread().getName();
        while (count<100){
            System.out.println(name+"-->count:"+count++);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程同步机制:

当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作。

1.加互斥锁synchronized,可以放在代码块,或者修饰方法

public class SellTickets {
    //模拟售票
    public static void main(String[] args) {
        ChildRunnable childRunnable = new ChildRunnable();
        new Thread(childRunnable, "pdd").start();
        new Thread(childRunnable, "tb").start();
        new Thread(childRunnable, "wx").start();
    }
}
class ChildRunnable implements Runnable {
    //总票数30
    private int tickets = 30;
    private boolean flag=true;
    @Override
    public void run() {
        while (sell()){}
    }
    //加锁,当一个线程进入这个方法的时候,其他线程没法进去
    private synchronized boolean sell() {
        String name = Thread.currentThread().getName();
            if (tickets < 1) {
                System.out.println(name+": 票卖完啦");
                return false;
            }
            System.out.println(name + "正在卖第" + (tickets--) + "张票,剩余票数"+tickets);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
}

修饰代码块:利用this对象

  private void sell() {
        synchronized (this){
            String name = Thread.currentThread().getName();
            if (tickets < 1) {
                System.out.println(name + ": 票卖完啦");
                return;
            }
            System.out.println(name + "正在卖第" + (tickets--) + "张票,剩余票数" + tickets);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

锁静态方法:锁的对象应当为当前类.class

public static void sell() {
        synchronized (ChildRunnable.class){
           //想锁的内容
        }
    }

所个线程都占用了对方的锁资源,但不肯相让,会出现死锁,在编程中应当避免死锁的产生

实现callable接口 重写call方法,放入FutureTask

public class Competition {
    public static void main(String[] args) {
        //创建同一个比赛对象
        Runner runner = new Runner();
        //利用FutureTask来创建两个跑步对象,并且获取结果
        FutureTask<String> rbResult = new FutureTask<>(runner);
        //兔子开始跑步
        new Thread(rbResult, "兔子").start();
        FutureTask<String> ttResult = new FutureTask<>(runner);
        //乌龟开始跑步
        new Thread(ttResult, "乌龟").start();
        try {
            //比赛结果已经出来了,下面两个输出的结果是一样的
            String rbName = rbResult.get();
            String ttName = ttResult.get();
            System.out.println(rbName);
            System.out.println(ttName);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class Runner implements Callable<String> {
    //比赛的胜利者
    private String winner;
    @Override
    public String call() throws Exception {
        String name = Thread.currentThread().getName();
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + "跑了" + i + "米");
            //如果胜利者不为空,停止
            if (winner != null) break;
        }
        //跑到100米停止,并且获取胜利者
        if (winner == null)
            winner = name;
        return winner;
    }

}

创建n个线程,并发下载小说

下载类:

public class Down {
    private Down() {
    }
    public static void downLoad(String sour, String dest) {
        File destPath=new File(dest);
        try (
                BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(sour).openStream()));
                BufferedWriter writer = new BufferedWriter(new FileWriter(destPath))
        ) {
            String s;
            while ((s=reader.readLine())!=null){
                writer.write(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

下载线程类:

public class DownNovelThread extends Thread {
    private String sourFile;
    private String destFile;

    public DownNovelThread(String sourFile, String destFile) {
        this.sourFile = sourFile;
        this.destFile = destFile;
    }
    @Override
    public void run() {
        Down.downLoad(sourFile,destFile);
    }
}

开启多线程:

private static void demo2() {
    String[] novels = {
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/65O7zQiNyeW2uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/ttY1dryd7P62uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/eWetL7lvrXf4p8iEw--PPw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/3fOjFW-QgXuaGfXRMrUjdw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/65O7zQiNyeW2uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/ttY1dryd7P62uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/eWetL7lvrXf4p8iEw--PPw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/65O7zQiNyeW2uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/ttY1dryd7P62uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/eWetL7lvrXf4p8iEw--PPw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/3fOjFW-QgXuaGfXRMrUjdw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/65O7zQiNyeW2uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/ttY1dryd7P62uJcMpdsVgA2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/eWetL7lvrXf4p8iEw--PPw2/",
            "https://read.qidian.com/chapter/p36QbYfOfTVdi5-cWpsVtA2/3fOjFW-QgXuaGfXRMrUjdw2/"
    };
    List<Thread> threads=new ArrayList<>(10);
    for (int i = 0; i < novels.length; i++) {
        String destPath="resource/"+(i+1)+".txt";
        DownNovelThread thread=new DownNovelThread(novels[i],destPath);
        threads.add(thread);
    }
    threads.forEach(Thread::start);
}

解决线程安全的方案

并行 vs 并发:

都是在同一时间做多个任务

并行:同一时刻,多个任务同时执行,多核cpu可以实现并行

并发:同一时刻多个任务交替执行,造成一种貌似同时的错觉,简单地说,单核cpu实现的多任务就是并发---------> parallel

用户级别的并发:高并发、高性能、高可用———> concurrent

对于tomcat:最高200个并发,mysql:最高151个并发

同步 vs 异步

同步:一个新任务的开始必须等待另外一个任务的结束 容易出现线程阻塞

异步:一个任务的开始不需要等待另外一个任务结束 ajax(异步刷新页面)

阻塞 vs 非阻塞

同步必然会出现阻塞

输入流就是一种阻塞(阻塞IO:BIO)不输入就无法继续下面程序

BIO:同步阻塞IO

NIO:同步非阻塞IO

AIO:异步非阻塞IO

进程 vs 线程

进程:一个应用软件就是一个进程 占据CPU和内存,当有一个线程死掉了,整个进程就死掉了

线程:比进程小的执行单元:一个进程里面有多个线程单元。线程与线程之间是相互独立的—->独有的线程栈,线程之间的通信更加方便,jvm默认至少两个线程:main 线程、GC守护线程

同步非阻塞: 多个线程同时执行   不会出现阻塞
同步阻塞:  多个线程都不做任务  

异布阻塞:  多个线程并发执行:都阻塞
异步非阻塞:多个线程并发执行:都正常执行
posted @ 2022-12-06 23:37  Liku007  阅读(54)  评论(0编辑  收藏  举报