java考点之程序看多线程

真没把java当回事,但事实上学起来还是挺费劲的,要记的东西太多了,如何学呢?还是那句话,能学多少算多少,但看了不能白看,关键是要用起来!

如题:2022年4月,第31题:要求写出程序输出结果。

public class Test31 implements Runnable {//这里创建线程显然 不是继承Thread子类,而是通过实现Runnable接口,然后写run方法
    public void run() {
        try {
            System.out.println("run()-<1>");
            Thread.sleep(100);
            System.out.println("run()-<2>");
        } catch (InterruptedException x) {
            System.out.println("run()-<3>");
            return;
        }
        System.out.println("run()-<4>");
        System.out.println("run()-<5>");
    }

    public static void main(String[] args) {
        Test31 one = new Test31();//为什么java类中,可以自己new自己,实则是main比较特殊,程序的入口,编译时不考虑位置的(与这个类没有关联),这里写到类里,只是图方便,不需要import而矣。
        Thread other = new Thread();//生成一个Thread实例,产生一个线程,
        Thread third = new Thread(one);//初始化时传递的线程体,也就是方法
        other.start();//调用了start方法,就进入了就绪态
        third.start();
        try {
            System.out.println("run()-<6>");
            Thread.sleep(200);
        } catch (InterruptedException x) {
//            System.out.println("run()-<8>");
        }
        System.out.println("main-<7>");
        other.interrupt();//使用interrupt来终止线程,如果是终止阻塞态线程会抛出一个interruptException异常
    }
}

程序运行结果,先运行主线程run()-<6>,再运行创建的线程,run()-<1>、run()-<2>、run()-<4>、run()-<5>,最后,主线程sleep(200)的时间到后,继承运行,输出run()-<7>.题目给出的other.interrupt并没有产生异常???

分析:

一直没学到这个层次,所以这块还是挺生的。有些东西是操作系统里边的,没必要重复,就从实际使用的角度出发,看看这些考点能在实际使用时怎么用?

基本知识,只列自己感觉生的或还不太理解的地方

线程的四种状态

新建,可运行态,阻塞态,死亡,这个与OS大同小异,了解下即可。关键是下面的如何用java实现这些状态?

java中线程的调度

java提供了一个线程调度器来监视和控制就绪状态的线程。调度采用的是抢占式,优先级高的线程优先执行。优先级数值越大,优先级越高(0~10)。java都有一个默认的主线程,也就是jvm启动的第一个线程,子线程是由应用程序创建的线程。优先级最低的线程是守护线程,用于监视其他线程工作的服务线程。

实现多线程的两种途径

一是继承 Thread 类,用 Thread 子类创建线程对象。二是在类中实现 Runnable接口,在类中提供 Runnable 接口的 run()方法。关键性工作有两个方面:一是编写线程的run()方法;二是建立线程实例。

继承Thread类创建线程

Thread类是用来创建线程和提供线程操作的类。分析其最复杂的构造方法:Thread(ThreadGroup g,Runnable target,String m),创建一个线程,名为 m,属于指定的线程组 g,target 是线程的目标,也就是要创建的线程,必须实现Runnable接口,在程序中实现run()方法;这个方法就是线程体,也就是要线程要实现的功能。

实际使用时,并不一定会调用这个最复杂的构造函数,也可能是最简单的无参构造函数,但流程是一样的。

使用模板:

public class ThreadClassTest10 {
    static Athread threadA;
    static Bthread threadB;

    public static void main(String[] args) {
        threadA = new Athread();
        threadB = new Bthread();
        threadA.start();
        threadB.start();
    }
}

class Athread extends Thread {
    public void run() {
        Date timeNow;//为了能输出当时的时间
        for (int i = 0; i <= 5; i++) {
            timeNow = new Date();//得到当前时间
            System.out.println("我是 threadA:" + timeNow.toString());
            try {
                sleep(200);
            } catch (InterruptedException e) {
            }
        }
    }
}
class Bthread extends Thread{
    public void run(){
        Date timeNow;//为了能输出当时的时间
        for(int i=0;i<=5;i++)
        {
            timeNow = new Date();//得到当前时间
            System.out.println("我是 threadB:" +timeNow.toString());
            try{sleep(100);}
            catch(InterruptedException e){}
        }
    }
}
使用Runnable接口创建线程

如题目所示:声明一个Runnable接口类,类内实现run()方法,然后创建线程Thread(//传递这个接口类),启动线程即可。更常用:

    // 定义一个可运行的类
    public class MyRunnable implements Runnable {
        public void run(){

        }
    }
    // 创建线程对象
    Thread t = new Thread(new MyRunnable());
    // 启动线程
    t.start();

线程挂起的几种方法

sleep()

是一个静态方法,也是可以引发InterruptException中断的,所以需要捕捉。

与yield方法的区别:

yield:只允许同优先级进程运行。

wait()

当前线程等待,直到其他线程调用该对象的notify()或notifyall()方法唤醒。

notify只是唤醒等待的第一个线程。

notifyall则唤醒等待的所有线程,重新参与竞争。

join()

将线程加入到当前正在执行的线程中先运行,如下例子:

public class ThreadJoinTest extends Thread {
    public ThreadJoinTest() {
    }
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("running the first loop " + i);
            }
            Thread.sleep(1000);
            for (int i = 6; i < 10; i++) {
                System.out.println("running the second loop" + i);
            }
        } catch (InterruptedException ie) {
            System.out.println("Sleep interrupted in run()");
        }
    }
    public static void main(String[] args) {
        try {
            ThreadJoinTest ti = new ThreadJoinTest();
            Thread t = new Thread(ti);
            t.start();
            t.join();//加入到主线程中运行,所以先要运行完t线程

            for (int i = 11; i < 15; i++) {
                System.out.println("running the third loop" + i);
            }
        } catch (InterruptedException ie) {
            System.out.println("Join interrupted in run()");
        }
        System.out.println("Exiting from Main");
    }
}

join是中断当前正在执行的线程,不一定非得是main线程,如下例子,便是先a,b再c,最后才是main:

public class ThreadJoin2Test extends Thread {
    public ThreadJoin2Test(String a) {
        super(a);//调用父类的方法
    }

    public void run() {
        System.out.println(this.getName());
    }

    public static void main(String[] args) {
        ThreadJoin2Test a = new ThreadJoin2Test("a");//创建一个名为a的子线程
        ThreadJoin2Test b = new ThreadJoin2Test("b");
        ThreadJoin2Test c = new ThreadJoin2Test("c");

        a.start();
        b.start();
        c.start();
        try {
            c.join();
        } catch (Exception e) {
        }
        System.out.println("This is Main!");
    }
}

关于中断线程 interrupt

书中没有过多的介绍,但确实题目中已经涉及到了,可以看作是一个考点。看看实际中如何用?详见程序注释,这里是打断一个正在挂起的线程,引发打断异常。

public class ThreadInterruptTest extends Thread  {
    public ThreadInterruptTest() {
    }
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {//打印完0~4
                System.out.println("running the first loop " + i);
            }
            Thread.sleep(10000);//子线程挂起,这个休眠时间要比主线程长,所以切换到主线程运行
            for (int i = 6; i < 10; i++) {
                System.out.println("running the second loop" + i);
            }

        } catch (InterruptedException ie) {
            System.out.println("Sleep interrupted in run()");
            for (int i = 11; i < 15; i++) {
                System.out.println("running the third loop" + i);
            }
        }
    }
    public static void main(String[] args) {
        ThreadInterruptTest ti = new ThreadInterruptTest();
        Thread t = new Thread(ti);
        t.start();//通过Thread子类的方式,启用了一个线程
        //Delay for a few seconds to let the other thread get going
        try {
            Thread.sleep(2500);//运行子线程
        } catch (InterruptedException ie) {
            System.out.println("Sleep interrupted in main()");
        }

        System.out.println("About to wake up the other thread");
        t.interrupt();//打断当前正在休眠的子线程,这样的话会抛出一个子线程异常
        System.out.println("Exiting from Main");//子线程异常执行完后,会返回到主线程执行,执行完结束
    }
}

线程间同步

主要就是使用wait和notify方法。简单了解下就可以

wait()和notify()都属于Object类的,java中的对象都带有这两个方法。

要使用wait()和notify()都必须首先对“对象” 进行 synchronized 加锁,脱离 synchronized 使用 wait()和notify() 会直接抛出异常.


线程间互斥

对象锁定标志

用关键字volatile来声明一个共享数据(变量)。只作用于变量,使用范围小
用关键字synchronized(同步的)来声明操作共享数据的一个方法或一段代码。定义临界段

volatile

主要作用在保证变量在内存中的可见性,任何修改都写回到内存,永远保证内存中的值 是最新的。但不能保证原子性

public class ThreadVolitileTest {
    static class MyTest {
        public volatile int number = 0;
//        public  void incr(){//不加synchronized,结果可能会不正确
        public synchronized void incr(){
            number++;
        }
    }

    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        for (int i = 1; i <= 10; i++){
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++){
                    myTest.incr();
                }
            }, "Thread"+String.valueOf(i)).start();
        }

        //等线程执行结束了,输出number值
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println("当前number:" + myTest.number);
    }
}
synchronized使用

synchronized同步的都是对象,锁的也都是对象,如下例子:

public class ThreadSynchronizedTest {
    public static void main(String[] args) {
        SellThread sell = new SellThread();
        Thread sell1 = new Thread(sell , "sellman1");
        Thread sell2 = new Thread(sell , "sellman2");
        Thread sell3 = new Thread(sell , "sellman3");
        sell1.start();
        sell2.start();
        sell3.start();//创建了三个线程
    }
}

class SellThread implements Runnable {
    private int i = 20;
    String a = "now ok!";

    public void run() {
        while (true) {
//            synchronized (a) {//synchronized定义的是一个临界段
            synchronized (this) {//synchronized定义的是一个临界段
                if (i > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + " sell " + i--);
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值