jdk线程的简单使用

一、线程的实现方式
方式一:继承Thread类
一个类只要继承了Thread类,并重写run()方法,则就可以实现多线程的操作。

public class ThreadDemo01 {

    public static void main(String[] args) {
        Thread1 thread1 = new Thread1("thread1");
        Thread2 thread2 = new Thread2("thread2");

        System.out.println(thread1.getName());
        System.out.println(thread2.getName());

        thread1.start();//启动线程
        thread2.start();
    }
}

class Thread1 extends Thread {
    public Thread1(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread1 : " + i);
        }
    }
}

class Thread2 extends Thread {
    public Thread2(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread2 : " + i);
        }
    }
}

方式二:实现Runnable接口
一个类只要实现了Runnable类,并重写run()方法,则就可以实现多线程的操作。

public class ThreadDemo03 {

    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyThread1(), "thread1");
        Thread thread2 = new Thread(new MyThread2(), "thread2");

        System.out.println(thread1.getName());
        System.out.println(thread2.getName());

        thread1.start();//启动线程
        thread2.start();
    }
}

class MyThread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread1 :" + i);
        }
    }
}

class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread2 :" + i);
        }
    }
}

两种启动方式的比较

将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法;

两种方法均需执行线程的start方法为线程分配必须的系统资源,调度线程运行并执行线程的run方法;在使用的时候使用实现接口优先(避免单继承);实现Runnable接口的方式能够实现资源的共享。

二、Thread的JDK源码分析

Thread thread1 = new Thread(new MyThread1(), "thread1");

Thread 构造方法源码如下:

public Thread(String name) {
    init(null, null, name, 0);
}

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
  …..
       this.name = name.toCharArray();
       this.target = target;
  …..
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}
public void run() {
    if (target != null) {
        target.run();
    }
}

1)当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量);

2)当使用继承Thread类来生成线程对象时,我们需要重写run()方法,因为Thread类的run()方法此时什么事情都没做(target==null);

3)当使用实现Runnable接口来生成线程对象时,我们需要实现Runnable接口的run()方法,然后使用new Thread(new MyThread())(假使MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run()方法就会调用MyThread类的run方法,这样我们自己编写的run()方法就执行了。

三、线程中成员变量和局部变量的使用

1)如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。

public class ThreadDemo04 {
    public static void main(String[] args) {
        HelloThread r = new HelloThread();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();
    }
}

class HelloThread implements Runnable {
    int i; //成员变量

    @Override
    public void run() {
        while (true) {
            System.out.println("number: " + this.i++);

            try {
                Thread.sleep((long) (Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (10 == this.i) {
                break;
            }
        }
    }
}

结果是:

number: 0
number: 1
number: 2
number: 3
number: 4
number: 5
number: 6
number: 7
number: 8
number: 9

2)如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。

public class ThreadDemo05 {
    public static void main(String[] args) {
        WorldThread r = new WorldThread();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();
    }
}

class WorldThread implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            System.out.println("number: " + i++);

            try {
                Thread.sleep((long) (Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (10 == i) {
                break;
            }
        }
    }
}

结果是:

number: 0
number: 0
number: 1
number: 1
……

四、卖票示例

50张票,3个窗口买,每个窗口相当于一个线程。

使用继承Thread类的方式卖票:

class MyThread1 extends Thread {
    private Integer num = 50;
    public MyThread1(String name) {
        super(name);
    }
    public void run() {
        int count = 0;
        for (int i = 1; i <= 200; i++) {
            if(num > 0){
                count ++;
                System.out.println(getName() + "卖出第--->" + num-- +"张票");
            }
        }
        System.err.println(getName()+"卖了"+ count +"张票" );
    }
}

getName()方法来自于Thread类中,故可以直接获得。

使用实现Runnable的方式卖票:

class MyThread2 implements Runnable{
    private Integer num = 50;
    public void run() {
        int count = 0;
        for (int i = 0; i < 200 ; i++) {
            if(num > 0){
                count ++;
                System.out.println(Thread.currentThread().getName()
                                + "卖出第--->" + num-- +"张票");
            }
        }
    System.err.println(Thread.currentThread().getName()
         +  "卖了"+ count +"张票" );
    }
}

由于该类实现了Runnable接口,该接口中并没有获取线程方法名称的方法,故只能采用Thread.currentThread().getName()这种最通用的方法来获得正在执行的线程名称。

两种卖票方式的比较:

继承方式:资源不能共享;一共卖出去150张票,每个窗口各50张;由于继承了Thread类之后就不能再继承其他类了;

接口方式:资源共享;一共卖出去50张票;方便以后扩展。

时间: 2024-10-13 11:04:03

jdk线程的简单使用的相关文章

多线程(三) java中线程的简单使用

============================================= 原文链接:多线程(三) java中线程的简单使用 转载请注明出处! ============================================= java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依旧是实现了Runnabel接口.考虑到java的

java线程的简单实现及方法

java线程: 线程是一个程序内部的顺序控制流. cpu实际上在一个时间点上,只执行一个.只不过我们把cpu分成了多个时间片,由于速度很快,我们看起来像是多个线程.. 就像你的时间分成几片,这样 整体看来做事情有规律,效率就会高,何况是cpu呢. 线程的创建和启动: 方法一: java线程通过java.lang.Thread类来实现. VM启动时会有一个主方法所定义的线程, 每一个线程是Tread对象通过它的run()方法来完成操作. 启动线程方式:Thread的start()方法. 如下代码:

iOS开发Swift篇(02) NSThread线程相关简单说明

iOS开发Swift篇(02) NSThread线程相关简单说明 一 说明 1)关于多线程部分的理论知识和OC实现,在之前的博文中已经写明,所以这里不再说明. 2)该文仅仅简单讲解NSThread在swift语境中的一些使用和注意点,别他. 3)本文涉及代码可以从https://github.com/HanGangAndHanMeimei/Code地址获得. 二 NSThread的基本使用和创建 1)基本用法(主线程|当前线程) 1 //1.获得执行该方法的当前线程 2 let currentT

Qt线程的简单使用系列

Qt线程的简单使用(四)--QSemaphore的使用 Qt线程的简单使用(三)--通过一个实例理解QMutex的使用 Qt线程的简单使用(二)--通过继承QThread()创建线程 Qt线程的简单使用(一)--通过QObject::moveToThread()创建线程

基于ThreadPoolExecutor,自定义线程池简单实现

一.线程池作用 在上一篇随笔中有提到多线程具有同一时刻处理多个任务的特点,即并行工作,因此多线程的用途非常广泛,特别在性能优化上显得尤为重要.然而,多线程处理消耗的时间包括创建线程时间T1.工作时间T2.销毁线程时间T3,创建和销毁线程需要消耗一定的时间和资源,如果能够减少这部分的时间消耗,性能将会进一步提高,线程池就能够很好解决问题.线程池在初始化时会创建一定数量的线程,当需要线程执行任务时,从线程池取出线程,当任务执行完成后,线程置回线程池成为空闲线程,等待下一次任务.JDK1.5提供了一个

jdk线程池主要原理

本文转自:http://blog.csdn.net/linchengzhi/article/details/7567397 正常创建一个线程的时候,我们是这样的:new thread(Runnable实现类).这里,thread是线程,Runnable实现类是业务逻辑,这样线程和业务逻辑紧紧绑定在一起. 采用线程池来处理的时候,我们动态生成若干个线程存于池中,但是这些线程要执行那些业务逻辑是不知道的,由于业务逻辑个数和具体的逻辑需要用户来指定,这些是变化的,我们需要自己编写并存于linkedLi

JDK线程池原理剖析

这篇文章不打算讲解什么是线程池?线程池怎么用?直接讲解原理 1.线程池关键参数解释 JDK 线程池的实现类是 ThreadPoolExecutor,构造函数关键参数解释如下: corePoolSize 核心线程,线程池维持的线程数量,即使没有任务执行也会维持这个数量不变,除非设置了 allowCoreThreadTimeOut 这个参数为 true maximumPoolSize 线程池中允许创建线程的最大数量 keepAliveTime 非核心线程的空闲等待时间,超过这个时间将被销毁 unit

查看jdk 线程 日志

命令:jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令 这些命令 必须 在 linux jdk bin 路径 下执行 eq: ./jstack 10303 即可  如果想把 内容打印到 文本里 即 ./jstack 10303 >111.log  打印 到 111.log文件 中,然后 sz  111.log 下载到本地查看. jmap -histo pid(查看实例) 怎么知道当前linux系统下的jdk路径,可以ps aux|grep jdk 可以看出路径,一般为/

JDK线程池的使用

转载自:https://my.oschina.net/hosee/blog/614319: 摘要: 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 线程池的基本使用 2. 扩展和增强线程池 3. ForkJoin 1. 线程池的基本使用 1.1.为什么需要线程池 平时的业务中,如果要使用多线程,那么我们会在业务开始前创建线程,业务结束后,销毁线程.但是对于业务来说,线程的创建和销毁是与业务本身无关的,只关心线程所执行的任务.因此希望把尽可能多的cpu用在执行任务上