线程的创建以及start、run、join、yield方法的区别

什么是线程

线程是操作系统能够进行运算调度的最小单位,一个程序运行就是一个进程,一个进程包括至少一个线程

线程和进程有什么区别

线程是进程的子集;进程占据较多的系统资源,线程仅占用一些必不可少的系统资源;进程之间的内存空间是独立的,线程之间是共享的;进程的上下文切换代价较大,线程之间的切换代价很小。

synchronized关键字

大家一直习惯称它为同步,我觉得将之称为按照顺序执行更合适,线程是具有并发性的,因此两个线程一起运行的时候可能会出现数据错误的情况,比如下面,我每次执行线程时同时执行一个sleep方法,模拟线程执行较慢的情况(注意:在加锁的情况下,执行sleep方法不会释放锁哦);
public class Test implements Runnable{
    private int i=0;
    @Override
    public void run() {
       while (i<5){


            System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);
            try {
                Thread.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
           i=i+1;
        }
    }
    public static void main(String[] args) {
        Runnable runnable = new Test();
        Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程
        Thread thread2 = new Thread(runnable,"******线程二");
        thread1.start();
        thread2.start();
    }


}

运行结果如下:
可以看到,我卖出了两张票号为0的票,自然就出了错误,为了避免这种错误,我们可以引入synchronized关键字
如下
public synchronized void run() {
    System.out.println(Thread.currentThread().getName()+"想要售卖票------");
   while (i<5){
        System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);
        try {
            Thread.sleep(1);
        }catch (Exception e){
            e.printStackTrace();
        }
       i=i+1;
    }
}

如下我们可以看到,不会卖出错乱重复的票了
synchronizedTest可以在普通方法,静态方法,代码块中使用。作用在普通方法中时,锁的是当前实例对象;作用在静态方法时,锁的是这个类(静态方法属于类,仅此一个嘛);
作用在代码块时,代码块中是什么,自然锁的就是什么,如下:
锁的是当前实例对象
public void run() {
    System.out.println(Thread.currentThread().getName()+"想要售卖票------");
    synchronized (this){
        while (i<5){
            System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);
            try {
                Thread.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
            i=i+1;
        }
    }
}

也可以锁当前这个类
public void run() {
    System.out.println(Thread.currentThread().getName()+"想要售卖票------");
    synchronized (Test.class){
        while (i<5){
            System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);
            try {
                Thread.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
            i=i+1;
        }
    }
}

锁的概念

这里的锁的概念我刚开始一直不太明白,说下我自己的理解。首先个人认为锁有两个关键点,锁了谁,锁了哪里。
举个例子,交警叔叔在立水桥路口查未佩戴头盔骑电动车的人,不符合要求的不允许通行,这其实就是一个锁。锁了谁呢,骑电动车未佩戴头盔的人;锁了哪里呢,立水桥路口。
那么如果我没骑电动车且没佩戴头盔,那交警叔叔也不会查处我的,因为我不是锁的对象(锁普通方法时,仅锁当前实例对象,再生成一个实例对象时,不会上锁);
如果我骑了电动车还未佩戴头盔,那么我可以不走立水桥路口啊,因为别的地区没上锁(锁代码块时,代码块之外的代码仍然可以被线程并发访问)
如果我骑了电动车还带着头盔自然可以从立水桥通过,因为我是获得了资源的。
如果我骑了电动车未佩戴头盔还从立水桥过,那么你未获得权限,成功被拦截,不允许通行(不允许访问上锁的资源)

start、run、join、yield方法的区别

run方法只是Runnable接口的一个普通方法,运行它并未开启真正的线程
start方法是Thread类的方法,运行它才是开启了线程,开启后会调用run方法
join方法是在当前线程调用另一个线程的join方法,那么会阻塞当前线程,直到另一个线程执行完毕,才会继续执行当前线程
如下,首先main方法也是一个线程,在不加thread1.join()的情况下,由于线程的并发性,那么打印最后打印出来的i的值很可能为0,加上thread1.join()后,则先执行thread1这个线程,再执行main方法的线程,最后打印的数据为5
public class Test implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<5){
            i=i+1;
        }
    }
    public static void main(String[] args) throws Exception{
        Runnable runnable = new Test();
        Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程
        thread1.start();
        thread1.join();
        System.out.println(((Test) runnable).i);
    }
}

yield是Thread类的一个静态方法,会让当前线程交出cpu资源,让线程重新竞选执行机会,如下,如果去掉了Thread.yield(),如果计算机运行速度较快的话,则可能一直是线程一运行直至运行完毕,加上此方法后,则使线程二在每次i++之后都能够有获取cpu资源的机会
public class Test implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<10){
            System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);
            i++;
            Thread.yield();
        }
    }
    public static void main(String[] args) {
        Runnable runnable = new Test();
        Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程
        Thread thread2 = new Thread(runnable,"******线程二");
        thread1.start();
        thread2.start();
    }
}

守护线程

守护线程和普通线程的创建没有太大区别,在生成一个线程实例的时候,调用setDaemon(true)即可设置为守护线程,注意需要在start方法之前。
当一个进程内的非守护线程执行完成后,无论有没有正在执行的守护线程,JVM都会停止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值