听说这四个概念,很多 Java 老手都说不清 (2)

Runnable:当调用了线程类的 start() 方法, 那么这个线程就会从 New 状态转换到 Runnable 状态。这就意味着这个线程要准备运行了。但是,如果线程真的要运行起来,就需要线程调度器来调度执行这个线程。但是线程调度器可能忙于在执行其他的线程,从而不能及时去调度执行这个线程。线程调度器是基于 FIFO 策略去从线程池中挑出一个线程来执行的。

Blocked:线程可能会因为不同的情况自动的转为 Blocked 状态。比如,等候 I/O 操作,等候网络连接等等。除此之外,任意的优先级比当前正在运行的线程高的线程都可能会使得正在运行的线程转为 Blocked 状态。

Waiting:在同步块中调用被同步对象的 wait 方法,当前线程就会进入 Waiting 状态。如果在另一个线程中的同一个对象被同步的同步块中调用 notify()/notifyAll(),就可能使得在 Waiting 的线程转入 Runnable 状态。

Timed_Waiting:同 Waiting 状态,只是会有个时间限制,当超时了,线程会自动进入 Runnable 状态。

Terminated:线程在线程的 run() 方法执行完毕后或者异常退出run()方法后,就会进入 Terminated 状态。

为什么要使用多线程

大白话讲就是通过多线程同时做多件事情让 Java 应用程序跑的更快,使用线程来实行并行和并发。如今的 CPU 都是多核并且频率很高,如果单独一个线程,并没有充分利用多核 CPU 的优势。

重要的优势

可以更好地利用 CPU

可以更好地提升和响应性相关的用户体验

可以减少响应时间

可以同时服务多个客户端

创建线程有两种方式

通过继承Thread类创建线程

这个继承类会重写 Thread 类的 run() 方法。一个线程的真正运行是从 run() 方法内部开始的,通过 start() 方法会去调用这个线程的 run() 方法。

public class MultithreadDemo extends Thread { @Override public void run() { try { System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { MultithreadDemo multithreadDemo = new MultithreadDemo(); multithreadDemo.start(); } } }

通过实现Runnable接口创建线程

我们创建一个实现了 java.lang.Runnable 接口的新类,并实现其 run() 方法。然后我们会实例化一个 Thread 对象,并调用这个对象的 start() 方法。

public class MultithreadDemo implements Runnable { @Override public void run() { try { System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new MultithreadDemo()); thread.start(); } } } 两种创建方式对比

如果一个类继承了 Thread 类,那么这个类就没办法继承别的任何类了。因为 Java 是单继承,不允许同时继承多个类。多继承只能采用接口的方式,一个类可以实现多个接口。所以,使用实现 Runnable 接口在实践中比继承 Thread 类更好一些。

第一种创建方式,可以重写 yield()、interrupt() 等一些可能不太常用的方法。但是如果我们使用第二种方式去创建线程,则 yield() 等方法就无法重写了。

同步

同步只有在多线程条件下才有意义,一次只能有一个线程执行同步块。

在 Java 中,同步这个概念非常重要,因为 Java 本身就是一门多线程语言,在多线程环境中,做合适的同步是极度重要的。

为什么要使用同步

在多线程环境中执行代码,如果一个对象可以被多个线程访问,为了避免对象状态或者程序执行出现错误,对这个对象使用同步是非常必要的。

在深入讲解同步概念之前,我们先来看看同步相关的问题。

class Production { //没有做方法同步 void printProduction(int n) { for (int i = 1; i <= 5; i++) { System.out.print(n * i+" "); try { Thread.sleep(400); } catch (Exception e) { System.out.println(e); } } } } class MyThread1 extends Thread { Production p; MyThread1(Production p) { this.p = p; } public void run() { p.printProduction(5); } } class MyThread2 extends Thread { Production p; MyThread2(Production p) { this.p = p; } public void run() { p.printProduction(100); } } public class SynchronizationTest { public static void main(String args[]) { Production obj = new Production(); //多线程共享同一个对象 MyThread1 t1 = new MyThread1(obj); MyThread2 t2 = new MyThread2(obj); t1.start(); t2.start(); } }

运行上面的代码后,由于我们没有加同步,可以看到运行结果非常混乱。
Output:
100 5 10 200 15 300 20 400 25 500

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxgzz.html