欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

读《java多线程核心技术》有感--第一章 java多线程技能

程序员文章站 2022-05-06 07:53:45
...

本章内容:
读《java多线程核心技术》有感--第一章 java多线程技能

关键技术:
1 线程的启动
2 如何让线程暂停
3 如何使线程停止
4 线程的优先级
5 线程的安全问题


1.1 进程和多线程的概念及线程优点

简单地说:一个正在运行的应用程序就是一个进程,比如任务管理器里面的每一个进程。

读《java多线程核心技术》有感--第一章 java多线程技能

然后说线程:线程就是进程在任务进行中的子任务,比如QQ.exe里面的视频线程,下载文件线程,表情包线程。

也就是说将进程拆分成多个线程,这样的好处是可以合理的尽可能的使用CPU资源,同时CPU切换任务很快,给我们的感觉多任务同时进行,用户体验好。

1.2 使用多线程

下面看一个代码:
读《java多线程核心技术》有感--第一章 java多线程技能

主线程main是JVM创建的,我要说的是main()与线程名称毫无关系,只是名字相同而已。


1.2.1 继承Thread类

具体代码:

package com.wuk.demo;

public class Test02 extends Thread{

    @Override
    public void run() {

        System.out.println("正在学习多线程");
    }

    public static void main(String[] args) {

        Test02 test02=new Test02();

        test02.start();

        System.out.println("结束");
    }

}

结果:

结束
正在学习多线程

从这里可以看出,线程调用的随机性。

下面再演示一下线程的随机性。

package com.wuk.demo;

public class Test03 extends Thread{

    @Override
    public void run() {

        for(int i=0;i<5;i++){

            int time=(int) (Math.random()*1000);
            try {
                Thread.sleep(time);

                System.out.println("run="+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {

        Test03 test03=new Test03();
        //设置线程名字
        test03.setName("test03");
        test03.start();
        for(int i=0;i<5;i++){

            int time=(int) (Math.random()*1000);
            try {
                Thread.sleep(time);

                System.out.println("main="+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

结果如下:

run=test03
main=main
run=test03
main=main
run=test03
run=test03
main=main
run=test03
main=main
main=main

start()方法就是通知线程规划器,该线程已经准备好,等待调用run()方法,其实就是让系统安排一个时间来调用该对象的run()方法,如果直接调用run()方法,就不会涉及到线程规划器了,就是同步而非异步。
还要注意,调用start()的顺序不是线程运行的顺序,具体的调度顺序是根据cpu分配的时间片段。


1.2.2实现Runnable()接口

具体代码如下:

package com.wuk.demo;

public class Test03 implements Runnable{

    @Override
    public void run() {

        System.out.println("学习多线程");
    }

    public static void main(String[] args) {

        Test03 test03=new Test03();

        new Thread(test03).start();
        System.out.println("结束");
    }
}

结果:

结束
学习多线程

注意看和继承Thread的线程启动的区别,实现Runnable接口的启动方式还是要借助Thread对象。

看一下Thread的源码:
读《java多线程核心技术》有感--第一章 java多线程技能

Thread 实现了Runnable接口 这样的话我们可以把一个线程的run()方法交给另一个线程执行。


1.2.3 实例变量与线程

资源共享问题

看具体代码:
代码1: 资源不共享

package com.wuk.demo;

public class Test05 extends Thread {

    private int count = 10;

    public Test05(String name) {
        // 线程名称
        this.setName(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= count; count--) {

            System.out.println(Thread.currentThread().getName() + "电影院卖出第"
                    + count + "张票");
        }
    }

    public static void main(String[] args) {

        Test05 test0501 = new Test05("A");
        Test05 test0502 = new Test05("B");
        Test05 test0503 = new Test05("C");

        test0501.start();
        test0502.start();
        test0503.start();

    }

}

结果:

B电影院卖出第10张票
A电影院卖出第10张票
C电影院卖出第10张票
A电影院卖出第9张票
B电影院卖出第9张票
B电影院卖出第8张票
B电影院卖出第7张票
A电影院卖出第8张票
C电影院卖出第9张票
A电影院卖出第7张票
B电影院卖出第6张票
B电影院卖出第5张票
A电影院卖出第6张票
C电影院卖出第8张票
A电影院卖出第5张票
B电影院卖出第4张票
A电影院卖出第4张票
C电影院卖出第7张票
A电影院卖出第3张票
B电影院卖出第3张票
B电影院卖出第2张票
A电影院卖出第2张票
C电影院卖出第6张票
A电影院卖出第1张票
B电影院卖出第1张票
C电影院卖出第5张票
C电影院卖出第4张票
C电影院卖出第3张票
C电影院卖出第2张票
C电影院卖出第1张票

代码2 :资源共享:

package com.wuk.demo;

public class Test05 extends Thread {

    private static int count = 10;

    @Override
    public  void run() {
        for (int i = 1; i <= count; count--) {

            System.out.println(Thread.currentThread().getName() + "电影院卖出第"
                    + count + "张票");
        }
    }

    public static void main(String[] args) {

        Test05 test05 = new Test05();

        Thread t1=new Thread(test05,"A");
        Thread t2=new Thread(test05,"B");
        Thread t3=new Thread(test05,"C");
        t1.start();
        t2.start();
        t3.start();
    }

}
A电影院卖出第10张票
B电影院卖出第10张票
C电影院卖出第10张票
B电影院卖出第8张票
A电影院卖出第9张票
B电影院卖出第6张票
C电影院卖出第7张票
B电影院卖出第4张票
A电影院卖出第5张票
A电影院卖出第1张票
B电影院卖出第2张票
C电影院卖出第3张票

这里之所以出现同时卖第十张票的原因是出现并发问题,但是这里很显然达到了资源共享。

线程安全问题,

package com.wuk.demo;

public class Test05 extends Thread {

    private static int count = 3;

    @Override
    public  void run() {

            if(count<=0){
                System.out.println(Thread.currentThread().getName()+"无票");
                return;
            }
            System.out.println(Thread.currentThread().getName() + "电影院卖出第"
                    + count-- + "张票");      
    }

    public static void main(String[] args) {

        Test05 test05 = new Test05();

        Thread t1=new Thread(test05,"A");
        Thread t2=new Thread(test05,"B");
        Thread t3=new Thread(test05,"C");
        Thread t4=new Thread(test05,"D");
        Thread t5=new Thread(test05,"E");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }

}
A电影院卖出第3张票
B电影院卖出第3张票
D电影院卖出第2张票
E无票
C电影院卖出第1张票

AB电影院出现了线程安全问题,在相互不知情的情况下同时卖出第3张票。
处理办法如下:
在run()前面加上synchronized,这样保持一个线程执行的时候,其他线程在外面等候,结果如下:

A电影院卖出第3张票
C电影院卖出第2张票
D电影院卖出第1张票
B无票
E无票

1.3 currentThread()方法

currentThread()方法返回代码段正在被哪个线程调用的信息。
比如:

Thread.currentThread().getName()

1.4 isAlive()方法

isAlive()判断当前的线程是否处于活动状态。
活动状态:指的是线程已经启动且尚未终止,处于正在运行或者准备开始运行的状态。

package com.wuk.demo;

public class Test07 extends Thread {

    @Override
    public void run() {

        System.out.println("线程状态="+Thread.currentThread().isAlive());
    }


    public static void main(String[] args) {

        Test07 test07=new Test07();

        System.out.println("begin---"+test07.isAlive());
        test07.start();
        System.out.println("end---"+test07.isAlive());
    }


}

结果:

begin---false
end---true
线程状态=true

注意:end打印出来的结果也是true,但是该值不确定,因为是该线程尚未结束,所以输出true。

package com.wuk.demo;

public class Test07 extends Thread {

    @Override
    public void run() {

        System.out.println("线程状态="+Thread.currentThread().isAlive());
    }


    public static void main(String[] args) {

        Test07 test07=new Test07();

        System.out.println("begin---"+test07.isAlive());
        test07.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("end---"+test07.isAlive());
    }


}

结果:

begin---false
线程状态=true
end---false

因为在一秒之前线程结束了。


1.5 sleep()方法

sleep()让正在执行的线程(this.currentThread())休眠,暂停线程。


1.6 getId()方法

getId()获得线程的唯一标识。


1.7 停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是说放弃当前操作。
停止的三种方法:
(1)使用退出标志,使得线程正常退出,也就是run()方法执行完成后线程终止。
(2)使用stop()终止线程,不推荐。
(3)使用interrupt()中断线程。


1.7.1 停不了的线程

案例:
使用interrupt()方法停止线程,但是他的效果并不会像break那样直接停止了。它仅仅只是给线程打了一个停止的标志,并不是真正停止了。


判断线程是否是停止状态

相关标签: 多线程 java