java多线程

多线程

进程

一个程序对应一个进程

线程

进程是程序的执行路径,程序当前不同动作

并发

充分利用CPU,线程切换速度快,系统运行多线程程序单核

并行

多核,同时做不同的事

线程的五个状态

新建状态就绪状态运行状态阻塞状态终止状态
new Threadtread.starttread.runtread.sleep/waittread.stop

jion():线程强制运行

其中主线程比子线程的优先级高,可以通过让某个线程强制先运行完成

sleep(秒数):线程睡眠

某个线程主动阻塞,让出资源给其他线程运行,时间到后自动进入到线程队列等待运行

interrupt();中断线程

主动将线程中断

isinterrupted();判断线程是否中断

返回线程是否中断状态

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 10000; i++) {
            if (Thread.currentThread().isInterrupted()){
                break;
            }
            System.out.println(i);
        }
});
    t1.start();
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //中断线程
    t1.interrupt();
    //判断线程是否中断
    System.out.println(t1.isInterrupted());
}

在线程睡眠时将线程中断

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 1000; i++) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //抛出异常,用来结束线程
            }
            System.out.println(i);
        }
    });
    t1.start();
    Thread.sleep(1000);
    t1.interrupt();//如果线程在线程休眠时被中断,无法结束,可以通过抛出异常方法结束
    System.out.println(t1.isInterrupted());
}

这将会出现睡眠异常,sleep()被打断,所以sleep就直接抛出终止异常

创建子线程

使用Runnable类先创建,然后传入Thread里面
启动线程即可

使用内部类进行

写资源类,调用时才写线程,写一个内部类,内部类强制重写抽象方法
Thread.run();纯粹是调用run方法,不开启新线程,相当于单线程运行
匿名内部类

new Thread("窗口"+i){
    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {
            ticket.sale();
        }
    }
}.start();

使用接口进行创建

当接口里面只有一个方法时可以使用箭头函数写
开发通常使用接口来创建

new Thread(() -> {
    for (int j = 0; j < 100; j++) {
            ticket.sale();
        }
},"窗口"+i).start();

多个线程同时调度一个资源

加上一个同步锁synchronized,保证线程同步,作用在方法上,此方法同一个时刻只允许一个线程执行

同步方法

作用在方法上,此方法同一时刻只允许一个线程执行,其他线程进行等待,直到当前线程完成
效率太慢,一般不使用,因为不方便,让其他的线程等待了

public synchronized void sale(){
        if (num > 0){
            num--;
            System.out.println(Thread.currentThread().getName()+"还剩"+num+"张票");
        }
    }
同步对象

同步代码块,给一段代码上锁,锁可以是任何对象,但是必须是多个线程共同拥有的对象,即多个线程拥有的是同一把锁,这样子才可以起到互斥作用,上锁之后,代码块同时只能被一个线程执行

this表示当前对象,或者可以指定现在的是哪个类,对该对象进行上锁,直接类名.class

public static void sale1(){
    /**
     * 同步代码块,给一段代码上锁,锁可以是任何对象,
     * 但是必须是多个线程共同拥有的对象,
     * 即多个线程拥有的是同一把锁,这样子才可以起到互斥作用
     * 代码块同时只能被一个线程执行
     *
     * this表示当前对象,或者可以指定现在的是哪个类,直接类名.class
     */
    synchronized (Ticket.class){
        if (num > 0) {
            System.out.println(Thread.currentThread().getName()+"还剩"+(num--)+"张票");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
线程不安全

当线程不安全时,会造成其中的值出现混乱,可能多个线程对当前变量的原来的值进行操作,对变量操作还是使用同步对象,加上一个锁,可以保证安全

    /**
     * volatile修饰词,变量值发生改变后对其他线程可见
     * 可以保证可见性、有序性、不保证原子性
     * 1、可见性
     * 2、有序性
     * 3、原子性
     * 
     * synchronize关键字,保证原子性,有序性,
     * 使得每个线程都可以单独调用该代码块
     */
    private volatile int i;

volatile关键字

public class Test07 {
    private static volatile boolean flag = true;
    /**
     * volatile关键字的使用:当main线程改变属性值时,子线程立马可见
     * 主要为:让其他线程立马可见
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            while (flag) {
                System.out.println();
            }
        }).start();
        Thread.sleep(1000);
        flag = false;
    }
}
线程安全的操作

检验i++不是原子操作,在多线程环境下需要同步,类似的还有:ArrayList、HashMap等都不是线程安全的,vector,Hashtable线程安全

public class ArrayListTest {
    public static void main(String[] args) throws InterruptedException {
    //使用ArrayList时会出现相加的时候,到达不了多线程所加的数值
//        ArrayList<Integer> list = new ArrayList<Integer>();
        Vector<Integer> list = new Vector<Integer>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    list.add(j);
                }
            }).start();
        }
        Thread.sleep(5000);//休眠五秒
        System.out.println(list.size());
    }
}
线程同步例子——唤醒线程和等待线程

创建两个线程,交替输出0,1数字,不带缓冲区
main函数

/**
 * 创建两个线程,交替输出0,1数字,不带缓冲区
 */
public class Test08 {
    public static void main(String[] args) {
        NumberExchange ne = new NumberExchange();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ne.incr();	//每次都只调用一次这个方法
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"加法").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ne.decr();	//每次都只调用一次这个方法
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"减法").start();
    }
}

操作类

但是当前类在多个线程同时调用时,会出现虚假唤醒
比如多个加法类同时调用时,if只会判断一次,会出现虚假唤醒,就会出现大于1时还会继续调用该方法,造成大于1出现

public class NumberExchange {

    private int num;
//线程等待直到唤醒,必须写在同步方法或者同步代码块中,在指定的监听器对象上进行等待
    public synchronized void incr() throws InterruptedException {
        if (num > 0)        //大于零则线程等待,否则加1
            this.wait();     //需要同一个对象的方法,只有同一个对象才可以锁住
        num++;
        System.out.println(Thread.currentThread().getName()+"->"+num);
//唤醒减法线程,唤醒在制定监听器对象上等待的线程
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        if (num < 1)
            wait();
        num--;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        this.notifyAll();
    }
}

若是要解决虚假唤醒的情况,可以使用以下的方法
使用while循环判断是否满足条件,若是已经超过了,则继续等待
操作类

public class NumberExchange {

    private int num;
//线程等待直到唤醒,必须写在同步方法或者同步代码块中,在指定的监听器对象上进行等待
    public synchronized void incr() throws InterruptedException {
        while (num > 0)        //大于零则线程等待,否则加1
            this.wait();     //需要同一个对象的方法,只有同一个对象才可以锁住
        num++;
        System.out.println(Thread.currentThread().getName()+"->"+num);
//唤醒减法线程,唤醒在制定监听器对象上等待的线程
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        while (num < 1)
            wait();
        num--;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        this.notifyAll();
    }
}

今日总结

线程资源同步
线程中断
线程安全
线程等待
线程唤醒
线程虚假唤醒
线程完全唤醒

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值