线程的那些事情

进程与线程

定义

进程:进程是系统进行资源分配和调度的一个独立单位。
线程:进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

关系

一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

创建线程的两种方式

继承Thread类

步骤:

  1. 创建一个子类A继承Thread类。
  2. 在A中重写Thread类中的run()方法,将要执行的操作编写在方法内。
  3. 创建线程对象,并用start()方法启动。

注:start()方法中也采用了run()方法。但是如果我们直接调用run()方法,将不会开启新的线程。依旧是在原来的线程中。


public class Threadone extends Thread {

        @Override
        public void run() {
            //任务
        }

    public static void main (String[] args){
        Threadone a = new Threadone();
        a.start();
    }
}

实现Runnable接口

(方法一)步骤:

  1. 创建一个子类A实现Runnable接口。
  2. 实现run()方法。
  3. 创建A的对象以及线程的对象。
  4. 调用线程的start();
public class Threadone implements Runnable {

        @Override
        public void run() {
            //任务
        }

    public static void main (String[] args){
        Threadone a = new Threadone();
        Thread b = new Thread(a);
        b.start();
    }
}

(方法二)匿名内部类:

public class Threadone {
    public static void main (String[] args){

        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                //任务
            }
        });
        b.start();
    }
}

线程的那些方法

sleep()和yield()

  1. sleep()可以指定时间,但是yield()不可以。
  2. 都不会释放锁。
  3. sleep()允许优先级较低的获得运行机会,yield()不能。

wait(),notify(),notifyAll()

简介:

  1. 都是属于object类的方法。
  2. wait()方法可以指定时间也可以不指定。表示某个获取锁的线程释放掉当前的锁。然后该线程就会进入等待池中,等待唤醒。
  3. notify()会随机唤醒一个等待池中的线程,将它移到锁池中。这时候就可能这个线程依然不满足,还需要等待,就会造成死锁现象。
  4. notifyAll()会唤醒等待池中的所有线程,然后再竞争,其中一个线程可以获得锁。如果获得锁的线程仍不能执行,就把资源让给下一个。

注意:

  1. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
  2. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
  3. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
  4. notify ()和wait ()的顺序不能错,如果A线程先执行notify()方法,B线程在执行wait()方法,那么B线程是无法被唤醒的。

实现生产者和消费者问题
参考这篇博客
实现交替打印问题



public  class  Thread1 extends Thread{
    Num num;
    public Thread1(Num num) {
        this.num = num;
    }
    @Override
    public void run(){
        for (int i = 1;i<=10;i++){
            synchronized (num){
                while(num.i != 0 ){
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print("a");
                num.i = 1;
                num.notifyAll();
            }
        }
    }
}


public class Thread2 extends Thread{
    Num num;
    public Thread2( Num num) {
        this.num = num;
    }
    @Override
    public void run(){
        for (int i = 1; i <= 10 ; i++) {
            synchronized (num){
                while(num.i != 1 ){
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print("b");
                num.i = 0;
                num.notifyAll();
            }
        }
    }
}

    public static void main(String []agr){
        Num num =new Num();
        Thread a = new Thread1(num);
        Thread b = new Thread2(num);
        a.start();
        b.start();
   } 
}

注意:因为我们锁的对象必须是同一个,所以这里使用String,Integer都是不可以哒~所以自己写了Num类~

public class Num {
    int i = 0;

}

还可通过ReentrantLock,即获取锁和释放锁。

suspend()和resume()

为一对,分别是将线程挂起和恢复。现在已经不支持使用这两个了,因为suspend是不释放锁的,如果在resume之前,有一个方法需要使用到suspend中的同一个锁,将会造成“冰冻现象”。解决方法:就是设置一个标记位,当需要挂起的时候就wait,需要恢复就notify。

join()

用来同步。(可以用来实现控制线程按顺序执行)。看源码~

 public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) { //当线程活着的时候就一直等待。
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

线程的中断机制

关于线程的中断,有两个方法:

  1. public void interrupt();根据源码上给的注释,这个方法分为四种?情况,有一点看不太懂。第一种情况就是如果线程中断的不是自身,则会调用checkAccess()这个方法,并且抛出SecurityException。第二种情况是如果线程正在执行wait,join,sleep,那么就会抛出InterruptedException,并且清除它的中断状态。第三种情况就是IO阻塞的时候,中断状态会被设置,并且抛出ClosedByInterruptException???第四种就是 如果本身没有阻塞,那么只会设置中断状态。
  2. public boolean isInterrupted();判断线程的中断状态。
public boolean isInterrupted() {
        return isInterrupted(false);//默认false,不会清除中断状态
    }

    /**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

所以,当我们要中断一个线程的时候,仅仅使用 interrupt()是不够的。

public class TestThread {
    static class ThreadA extends Thread{
        @Override
        public void run() {
            for (int j = 0; j < 100; j++) {
                System.out.print(j); }
        }
    }
    public static void main(String []agr){
        ThreadA a = new ThreadA();
        a.start();
        a.interrupt();
    }
}

运行结果

0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
Process finished with exit code 0

那么,我们应该怎么中断一个正在运行的线程呢?可以选择配合isInterrupted();


public class TestThread {
    static class ThreadA extends Thread{
        int i = 0;
        @Override
        public void run() {
                while(!isInterrupted()){
                        System.out.print(i++);
            }
        }
    }
    public static void main(String []agr){
        ThreadA a = new ThreadA();
        a.start();
        try {
            Thread.currentThread().sleep(10);//让主线程先睡一下
        } catch (InterruptedException e) {
        }
        a.interrupt();
    }
}

运行结果

0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
Process finished with exit code 0

打印了一会儿之后就停止啦~说明我们中断线程成功~


那我们再来看看处于阻塞状态的情况


public class TestThread {
    static class ThreadA extends Thread{
        @Override
        public void run() {
            try {
                System.out.print("我睡啦");
                sleep(2000);
                System.out.print("我睡完啦");
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.print("我中断啦");
            }
        }
    }
    public static void main(String []agr){
        ThreadA a = new ThreadA();
        a.start();
        a.interrupt();
    }
}

运行结果

我睡啦我中断啦java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at TestThread$ThreadA.run(TestThread.java:9)

可以看到它可以成功中断我们的线程~

最后要说的话:其实就是我的一篇笔记啦(这么安慰一下自己,免得自己都嫌弃自己)0 0.最后许个愿吧,希望自己学习进度可以快一点!!路漫漫…黄雅倩快跑起来吧(¬︿̫̿¬☆)还有好多好多需要我去学的 而且今天想好思路的一个算法题还没写,明早一定要先写!哎,渣油。回寝室睡觉啦,明天又是元气满满的一天~!晚安

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值