Java多线程

多线程

多任务执行,多路径执行
优点

提高性能
提高效率

缺点

提高了复杂度

进程与线程之间的区别

进程:系统中的程序,一个进程之间可以包含1~n个线程,系统中资源分配的最小单位,每个进程都有自己的代码与数据空间,进程之间的切换开销较大
线程:程序中的顺序流,线程是cpu调度与执行的最小单位,多个线程之间共享进程的代码与数据空间,每一个线程都有自己的程序计数器运行栈,程序之间切换开销较小,一个cpu同一时刻只能调度一个线程

线程学习内容:

线程的概念,优缺点
创建线程的方式  *****
线程的状态
线程安全问题    *****
线程通信

创建线程的方式

继承Thread,重写run方法+start开启线程
实现Runable接口,重写run方法+start开启线程
实现Callable接口,重写call+线程池

继承Thread,重写run方法+start开启线程
代码:

public class Class001_Thread  extends  Thread{
    @Override
    public void run() {
        for (int i=1;i<=20;i++){
            System.out.println("一边打游戏");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

public static void main(String[] args) {
    //创建线程对象
    Class001_Thread th=new Class001_Thread();
    //调用start开启线程
    th.start();
    for (int i=1;i<=20;i++){
        System.out.println("一边上课");
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

实现Runable接口,重写run方法+start开启线程
优点:

        接口多实现,类的单继承
        实现资源共享

重写方法对异常抛出的要求:

    重写方法上抛出的异常类型<=被重写方法上异常的抛出类型

代码:

public class Class002_Thread implements Runnable{
public static void main(String[] args) {
    //创建线程
    Thread th=new Thread(new Class001_Thread());
    //开启线程
    th.start();
    for (int i=1;i<=20;i++){
        System.out.println("一边陪刘亦菲");
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Override
public void run() {
    for (int i=1;i<=20;i++){
        System.out.println("一边打游戏");
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

开启线程第三种方式 : (了解)
实现juc包下的Callable接口,重写call方法 + 线程池
优点:

call方法可以抛出异常,可以定义返回值,run方法不可以

代码:

public class Class005_Racer implements Callable<Integer> {
    //记录赢的人的名字
    private String winner = null;  //控制游戏结束,线程的结束

/*
    返回值: 参赛者的总步数
 */
@Override
public Integer call() {
    for(int i=1;i<=100;i++){
        try {
            Thread.sleep(2);  //模拟跑步所耗时长
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步....");
        if("pool-1-thread-1".equals(Thread.currentThread().getName()) && i%10==0){
            try {
                Thread.sleep(10); //如果是兔子,修改10ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //判断是否还要继续
        if(!checkOver(i)){
            return i;
        }
    }
    return null;
}

//判断是否进行下一次步
//返回值: false比赛结束  true比赛继续
public boolean checkOver(int step){
    if(winner!=null){
        return false;
    }
    if(step==100){
        winner = Thread.currentThread().getName();
        return false;
    }
    return true;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //一场比赛
    Class005_Racer racer = new Class005_Racer();
    //两个参赛者
    //1)创建一个固定大小2个线程的线程池--> 得到一个线程池提供的执行服务
    ExecutorService server = Executors.newFixedThreadPool(2);

    //2)提交任务  submit(Callable<T> task)
    Future<Integer> result1 = server.submit(racer);
    Future<Integer> result2 =server.submit(racer);

    //3)得到结果 V get()
    Integer num1 = result1.get();
    Integer num2 = result2.get();
    System.out.println(num1);
    System.out.println(num2);

    //4)关闭服务
    server.shutdown();
}
}

线程的状态

新生状态:new
就绪状态
运行状态:cpu调度
阻塞状态
终止状态

一个线程如果一旦进入终止状态,不可恢复
一个线程如果进入到阻塞状态,阻塞解除之后,不能直接恢复运行,会直接恢复到就绪状态,等待cpu的调度

如何让线程进入到就绪状态:

1.start()
2.阻塞解除
3.cpu的调度切换
4.yield礼让线程

如何让线程进入阻塞状态

1.sleep
2.join()插队线程
3.wait()等待
4.IO

如何让线程进入终止状态:

1.正常执行完毕
2.stop()过时—不推荐使用
3.通过标识判断

sleep 线程休眠

static void sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
static void sleep(long millis, int nanos) 执行ms+ns

当一个线程调度sleep进入睡眠状态,让出cpu的资源
抱着资源睡觉: 这个资源不是cpu的资源, 值的是对象的锁资源

作用:

放大问题出现的可能性
模拟网络延迟   

yield 礼让线程

 当线程执行到yield方法,会让出cpu的资源,同时线程会恢复就绪状态

代码:

 public class Class002_Yield  implements Runnable{
public static void main(String[] args) {
    Class002_Yield yd=new Class002_Yield();
    new Thread(yd,"A").start();
    new Thread(yd,"B").start();
}
@Override
public void run() {
    System.out.println(Thread.currentThread().getName()+"开始了");
    Thread.yield();
    System.out.println(Thread.currentThread().getName()+"结束了");
}
}

join() 插队线程

A线程执行过程中,如果B线程插队了,A线程就会进入到阻塞状态,等待插队线程执行完毕|等待执行的时间,A线程会恢复到就绪状态
void join() 等待这个线程死亡。
void join(long millis) 此线程最多等待 millis毫秒。
void join(long millis, int nanos) 此线程最多等待 millis毫秒加上 nanos纳秒。

代码:

   public class Class003_Join {
    public static void main(String[] args) {
        new Thread(new Father()).start();
    }
}
class  Father implements Runnable{
@Override
public void run() {
    System.out.println("想吸烟了。。。");
    System.out.println("给他钱,让儿子去买烟");
    Thread th=new Thread(new Son());
    th.start();
    try {
        th.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("接过烟,吸一口");
}
}
class  Son implements  Runnable{
@Override
public void run() {
    System.out.println("接过钱去买烟...");
    System.out.println("路上路过一个一家游戏厅....进去玩10s钟...");
    for (int i=1;i<=10;i++){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i+"s过去了");
    }
    System.out.println("赶紧去买烟...");
    System.out.println("把烟给老爸....");
}

}

中断线程

void interrupt() 为线程添加一个中断标识
boolean isInterrupted() 测试此线程是否已被中断,是否曾经调用过interrupt方法添加了中断标识
static boolean interrupted() 测试当前线程是否已被中断,是否曾经调用过interrupt方法添加了中断标识,同时复位标识
代码:

public class Class004_Interrupt implements Runnable {
@Override
public void run() {
    for (int i = 1; i <= 50; i++) {
        System.out.println(Thread.currentThread().isInterrupted());
        if (Thread.interrupted()) {
            System.out.println("当前线程已被中断。。。");
            System.out.println(Thread.currentThread().isInterrupted());
            break;
        }
        System.out.println(i);
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    Thread th=new Thread(new Class004_Interrupt());
    System.out.println(th.isInterrupted());
    //开启线程
    th.start();
    Thread.sleep(1);
    //为th线程添加中断标识
    th.interrupt();
    System.out.println(th.isInterrupted());
}
}

注意: 当调用sleep方法线程睡眠时 : InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态 。

获取线程状态

Thread.State getState() 获取线程状态
Thread.State 线程状态。 线程可以处于以下状态之一:

NEW : new Thread()
  尚未启动的线程处于此状态。
RUNNABLE : 就绪|运行
  在Java虚拟机中执行的线程处于此状态。
BLOCKED : 等待对象锁的阻塞状态
  被阻塞等待监视器锁定的线程处于此状态。
WAITING : wait(),join()等
  无限期等待另一个线程执行特定操作的线程处于此状态。
TIMED_WAITING : 与时间相关的等待,sleep(ms),join(ms),wait(ms)...
  正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
TERMINATED : 终止
  已退出的线程处于此状态。

代码

public class Class005_getState implements Runnable{
@Override
public void run() {
    for (int i=1;i<=20;i++){
        System.out.println("i="+i);
        if (i==10){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public static void main(String[] args) {
    Thread th=new Thread(new Class005_getState());
    System.out.println(th.getState());//new
    th.start();
    System.out.println(th.getState());
    while (true){
        System.out.println("循环判断中:"+th.getState());
        if (Thread.State.TERMINATED==th.getState()){
            break;
        }
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

线程的优先级

线程的优先级 :
方法线程优先执行的可能性

1~10 设置
     1最小 ->static int MIN_PRIORITY 线程可以拥有的最低优先级。
     10最大->static int MAX_PRIORITY 线程可以拥有的最大优先级。
     5默认值 ->static int NORM_PRIORITY 分配给线程的默认优先级。

int getPriority() 返回此线程的优先级。
void setPriority(int newPriority) 更改此线程的优先级。

代码:

public class Class006_Priority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"执行了");
    }

public static void main(String[] args) {
    Class006_Priority pr=new Class006_Priority();
    Thread th1=new Thread(pr,"A");
    Thread th2=new Thread(pr,"B");
    Thread th3=new Thread(pr,"C");
    //设置线程
    th1.setPriority(2);
    th2.setPriority(10);
    th3.setPriority(Thread.MAX_PRIORITY);

     //获取线程的优先级
    System.out.println(th1.getPriority());
    System.out.println(th2.getPriority());
    System.out.println(th3.getPriority());
    th1.start();
    th2.start();
    th3.start();
}
}

线程的分类

用户线程 : 默认线程为用户线程
   守护线程

守护线程 : 守护用户线程的
    当所有的用户线程全都执行完毕,守护线程直接结束
    垃圾回收机制典型守护线程
    void setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程。

线程安全

    当多个线程同时操作同一份资源,才有可能出现线程不安全问题

同步锁 synchronized : 有可能出现数据不安全的代码段,让多个线程排队执行
同步的使用:

同步条件 :  协调多个线程排队执行的条件  -->对象的锁
同步的代码 :  需要多个线程排队执行的代码

synchronized用法:

修饰方法 : 同步方法
      同步实例方法
            条件 : 调用成员方法的对象
            代码范围 : 整个方法体
      同步静态方法
            条件 : 锁类->锁的类的Class对象->一个类只有一个,加载到内存中究存在Class对象,不变,唯一
            代码范围 : 整个方法体

修饰块 : 同步块
       synchronized(条件){
            排队执行的代码;
       }

注意:

        同步的代码范围太大,效率太低
        同步的代码范围太小,锁不住,不安全

同步方法

在方法上通过synchronized关键字修饰方法

成员方法
    锁的是调用成员方法的对象
静态方法
    锁的是类的Class对象

优点: 简单,直接同步整个方法体
缺点: 代码范围太大,效率较低
代码:

//控制案例的数据安全
public class Class002_Web12306 implements Runnable{
//共享资源 100张票
int tickets = 100;
@Override
public void run() {
    while(true){
        if(!buyTicket()){
            break;
        }
        //放大cpu切换调度的可能性
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//一次购票的过程,每一个线程排队购票
//返回值 : true-->继续购买  false->结束购买
public synchronized boolean buyTicket(){
    if(tickets<=0){
        return false;
    }
    /*模拟一次购票的时间*/
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
    return true;
}
public static void main(String[] args) {
    Class002_Web12306 web = new Class002_Web12306();
    //创建线程
    Thread th1 = new Thread(web,"张三");
    Thread th2 = new Thread(web,"李四");
    Thread th3 = new Thread(web,"王五");

    //线程开启
    th1.start();
    th2.start();
    th3.start();
}
}

同步块

语法:

synchronized(条件){
        排队执行的代码;
    }

条件 : 类名.class | this | 资源

锁类: 锁类相当于锁住了这个类的所有对象,如果只想要锁当前类的某一个对象,建议锁那一个具体的对象this

线程通信

生产者消费者模式 : 生产者用来生产商品,消费者就是消费产品
Object类中:wait()等待,notify()唤醒,notifyAll()唤醒全部

wait() 等待,通过对象调用wait()方法让当前进入等待阻塞状态,会进入到调用wait方法对象的等待池中进行等待,等待被唤醒
       让出cpu的资源,同时释放对象的锁
notify() 唤醒 : 唤醒对象等待池中正在等待的线程,被唤醒的线程进入到就绪队列,需要被cpu调度同时获取对象锁才能执行

wait()与notify()必须使用在同步环境下,用来协调多线程对共享数据存储的问题,否则会抛出异常 IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。

代码:

人车公用街道 :
    街道   信号灯boolean: true绿灯  false红灯  ns南北  we东西

    人 : 走绿灯true  人走南北ns
    车 : 走红灯false 车走东西走向 we

练习 : 使用多线程实现,输出AB1CD2....YZ13
public class Class007_Street {
    public static void main(String[] args) {
        //共享街道
        Street street = new Street();
        new Thread(new Person(street)).start();
        new Thread(new Car(street)).start();
    }
}

//街道
class Street{
    //信号灯
    private boolean flag = false;

    public Street() {
    }

    public Street(boolean flag) {
        this.flag = flag;
    }
//ns -> 人
public synchronized void ns(){
    if(flag){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("人走");
        //信号灯变灯
        flag = false;
        //唤醒车线程
        this.notify();
        //自己等待
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//we -> 车
public synchronized void we(){
    if(!flag) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("车走");
        //信号灯变灯
        flag = true;
        //唤醒人线程
        this.notify();
        //自己等待
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
//人线程
class Person implements Runnable{
    private Street street = null;

public Person(Street street) {
    this.street = street;
}

@Override
public void run() {
   while(true){
       street.ns();
   }
}
}
//车线程
class Car implements Runnable{
    private Street street = null;

public Car(Street street) {
    this.street = street;
}

@Override
public void run() {
    while(true){
        street.we();
    }
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值