• 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张票;方便以后扩展。

  • 相关阅读:
    The Node.js Event Loop, Timers, and process.nextTick()
    Main event loop
    Why should I avoid blocking the Event Loop and the Worker Pool?
    HTML Standard系列:Event loop、requestIdleCallback 和 requestAnimationFrame
    在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
    多线程 主线程
    mvvm
    跨库事务
    nginx 死循环
    nginx proxy pass redirects ignore port
  • 原文地址:https://www.cnblogs.com/luogankun/p/3986681.html
Copyright © 2020-2023  润新知