线程

1.线程的创建方式

建立线程的方式有两种
1-通过继承Thread类,创建一个Thread类的子类XXThread
通过直接new XXXThread()的方式创建线程对象
2-通过实现Runnable接口的方式创建可运行的类XXXRunnable
通过创建一个Thread类的对象,将XXXRunnable对象作为参数传入到new Thread(这里)的构造方法中

两种方式代码如下:
第一种方式:

创建一个类继承Thread类
public class MyThread extends Thread {
// MyThread继承Thread
// Thread就是线程类,那么MyThread继承Thread后,
// MyThread就变成了线程
    @Override
    public void run() {
        super.run(); // 这里super调用父类的方法,就是null的
        // super指代父类的对象,调用父类的方法
        // this指代本类的对象(先调自己,再调从父类继承过来的)

        // 当MyThread线程对象被启动后(start后)
        // 就会执行这个方法
        try{
            Thread.sleep(2000);//2000指的是毫秒
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        system.out.println("我是一个子线程");
    }
}
在main方法中启动线程
public static void main(String args[]) {
    // 创建线程对象
    MyThread myThread = new MyThread();
    MyThread myThread1 = new MyThread();
    // 启动线程
    myThread.start();
    myThread1.start();
}

第二种方式:

创建一个类实现runnable
public class MyRunnable implements Runnable {
    // MyRunnable不是一个Thread
    // 是一个可运行的类
    @Override
    public void run() {
        for (int i= 0;i < 20; i++) {
            try{
                Thread.sleep(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            system.out.println("我是正在执行Runnable:" + i);
        }
    }
}
在main启动线程
public static void main(String args[]) {
    // 1.创建以XXXRunnable对象
    MyRunnable myRunnable = new MyRunnable();
    // 2.创建一个Thread对象
    // 并将上面创建的XXXRunnable对象传入构造方法中
    // public Thread(Runnable target)的构造方法
    // target是Runnable接口类型
    // 可以将收实现了Runnable接口的任何一个类的对象
    // 经常说成:它(指的就是Runnable接口)的实现类对象
    Thread t1 = new Thread(MyRunnable);
    t1.strat();
}

上面两种创建方式,我们可以通过创建类的匿名内部类,进行建立

通过实现Runnable接口来,创建线程并启动
匿名内部类(是Runnable接口的实现类对象) + 匿名对象

new Thread(new Runnable(){
    @Override
    public void run() {
        while(true) {
            system.out.println("这是第一个线程")
        }
    }
}).strat();

通过继承Thread类来建立线程

new Thread(){
    @Override
    public void run(){
        super.run();
        while(true){
            system.out.println("------");
        }
    }
}.start();

当两个或多个线程同时运行
线程是抢占式运行,谁抢到CPU的执行权,那么谁就执行

2.线程的生命周期

1-新建状态
就是new了一个线程对象
2-就绪状态(可运行,可执行状态)
线程调用.start()方法
3-运行状态(执行状态)
抢占到CPU的执行权,正在执行
4-阻塞状态
线程运行过程中调用一些方法进入阻塞状态
sleep():停止一段时间,这段时间不会去抢占CPU
join():等待XXX线程执行完毕,在进入等待抢占CPU
wait():持有锁对象的线程,释放锁对象
yield():是当前线程回到可执行状态
IO流阻塞:键盘输入,等待输入的状态
锁对象:线程ABCD这行同一方法,线程A拿到锁对象,执行方法,其他三个线程进入阻塞状态,直到线程A释放锁对象,其他三个线程进入到可执行的状态
5-死亡状态
程序异常
线程结束
退出run()方法

3.对象锁

在多线程并发时会出现问题
如代码所示

定义一个类
public Class DemoThread extends Thread{
    private static int cont = 1000;
    @Override
    public synchronized void run() {
        super.run();
        for(int i=0;i < 2500;i++){
            cont -= 1;
            system.out.println(cont);
        }
    }
}
创建一个main方法
public static void main(String[] args){
    DemoThread d1 = new DemoThread();
    DemoThread d2 = new DemoThread();
    DemoThread d3 = new DemoThread();
    DemoThread d4 = new DemoThread();
    d1.strat();
    // 取值为10000 做-1操作后为9999
    // 但是还没有输出9999呢
    // 就会被抢占执行权
    // 就会出现
    // 9999在9998后面或小于9999的数字的下面
    d2.strat();
    // d2取值为300,在做-1的操作后赋值给cont时
    // d3可能也取值到了300,做了一次-1的操作
    // 那么d2和d3这两次操作
    // 实际上只让cont减少了1
    // 所以可能出现四个线程执行完毕,结果大于0的情况
    d3.strat();
    d4.strat();
}

想要解决上述代码出现的状况,就可以使用同步关键字(对象锁)

锁是什么?
锁是一个对象
假如A线程县访问del方法
那么锁对象就被线程A持有
其他线程(B,C,D)就都不能进入到del方法中
当A线程执行完毕del方法,会将锁对象释放出来
这时候(B,C,D)线程在争夺执行权
谁抢到了,谁来执行del方法
谁执行del方法,那么谁就又先拿到了锁对象
这时候剩下的两个线程就又进不来del方法中了

使用synchronized关键字的两种方式:
-在方法声明上,使用synchronized关键字修饰
-使用synchronized代码块将要执行的代码包起来

// 当创建一个对象
Demo demo = new Demo();
// 启动两个线程,创建一个对象,分别调用自己的两个方法
new Thread(){
    @Override
    public void run(){
        demo.show();
    }
}.start();
new Thread(){
    @Override
    public void run(){
        demo.del();
    }
}.start();

show方法,del方法都是被synchronized关键字修饰的
而synchronized关键字修饰方法的锁对象
就是该方法的调用者
对象.方法();
这里调用show方法,与调用del方法的对象是一个
所以show方法的锁对象
与del方法的锁对象也就是一个了
而第一个线程已经进入了show方法
也就持有了锁线程
第二个线程,想要进入到del方法
需要等第一个线程,从show的方法中出来,释放锁对象的时候
才能进入到del方法中

死锁

有两把钥匙
有甲乙两个人各拿一把,甲拿A钥匙,乙拿B钥匙,分别走两条路
甲走的路上,有两道门上了锁分别为A,B,甲开了一道,停在了第二道们前,因为没有钥匙打开
乙走的路上,有两道门上了锁分别为B,A,乙开了一道,停在了第二道们前,因为没有钥匙打开
甲乙谁都没有办法打开第二道门,卡在了那,就叫做死锁

接口回调

接口实现类,通过接口类型传递,在很远的地方对象调用的方法,就是传递的对象,调用自己的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值