Java线程:什么是线程

简介: 一 基本概念   多任务:同一时刻运行多个程序的能力。每一个任务称为一个线程。可以同时运行一个以上线程的程序称为多线程程序。   Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。

一 基本概念

  多任务:同一时刻运行多个程序的能力。每一个任务称为一个线程。可以同时运行一个以上线程的程序称为多线程程序。

  Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行。

  一般常见的Java应用程序都是单线程的。比如,用java命令运行一个最简单的HelloWorld的Java应用程序时,就启动了一个JVM进程,JVM找到程序程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后,主线程运行完成。JVM进程也随即退出 。

  对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。

  进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

  线程是指进程中的一个执行流程,一个进程可以运行多个线程。比如java.exe进程可以运行很多线程。线程总是输入某个进程,进程中的多个线程共享进程的内存。

  Java中线程是指java.lang.Thread类的一个实例或线程的执行。使用java.lang.Thread或java.lang.Runnable接口编写代码定义、实例化、启动新线程。
  Java中每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行。main()方法运行在一个线程内,称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。
  线程分为两类:用户线程和守候线程。当所有用户线程执行完毕后,JVM自动关闭。但是守候线程却不独立与JVM,守候线程一般是有操作系统或用户自己创建的。
 
二 定义线程
1 扩展java.lang.Thread类以及实现java.lang.Runnable接口。
  此类中有run()方法,public void run(),如果该线程是独立的Runnable运行对象构造的,则调用该Runnable对象的run()方法;否则,该方法不执行任何操作。Thread的子类也应该重写该方法。
 
三 实例化线程
1 如果是扩展了java.lang.Thread类的线程,则直接调用new即可。
2 如果是实现了jav.lang.Runnable接口的类,则调用Thread的构造方法:
  Thread(Runnable target)
  Thread(Runnable target,String name)
  Thread(ThreadGroup group, Runnable target)
  Thread(ThreadGroup group, Runnable target, String name)
  Thread(ThreadGroup group, Runnable target, String name, long stackSize)
 
四 启动线程
  在线程的Thread对象上调用start()方法,而不是run()或别的方法。
  在调用start()方法之前,线程处于新状态中,新状态有一个Thread对象,但没有一个真正的线程。
  在调用start()方法之后,发生了一系列复杂的事情:  
    启动新的执行线程(具有新的调用栈);
    该线程从新状态转移到可运行状态;
    当该线程获得执行机会时,其目标run()方法将运行。
 
五 注意事项
  1 获取当前线程的对象的方法是:Thread.currentThread();
  2 当线程目标run()方法结束时该线程完成。
  3 一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
  4 线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。
  5 尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列一个队列的事实。
  6 尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
 
六 示例
  当点击start按钮时,程序从屏幕左上角弹出一个球,这个球开始移动,调用addBall方法,循环运行1000次move。每调用一次move,球就会移动一点,当碰到墙壁时,就会调整方向,但是这个程序有个弊端:当你想在移动1000次之前,就想退出程序,点击close发现,其仍在移动。
  Thread类的静态方法sleep()将暂停给定的毫秒数。调用Thread.sleep不会创建一个新线程,sleep是Thread类的静态方法,用于暂停当前线程活动。
   Bounce.java
 1 package Thread;
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import javax.swing.*;
 5 public class BounceThread {
 6     public static void main(String[] args){
 7         EventQueue.invokeLater(new Runnable(){
 8             public void run(){
 9                 JFrame frame=new BounceFrame();
10                 frame.setTitle("BounceFrame");
11                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12                 frame.setVisible(true);
13             }
14         });
15     }
16 }
17 /*class BallRunnable implements Runnable{
18     private Ball ball;
19     private Component component;
20     public static final int STEPS=1000;
21     public static final int DELAY=5;
22     public BallRunnable(Ball aBall,Component aComponent){
23         ball=aBall;
24         component=aComponent;
25     }
26     public void run(){
27         try{
28             for(int i=1;i<=STEPS;i++){
29                 ball.move(component.getBounds());
30                 component.repaint();
31                 Thread.sleep(DELAY);
32             }
33         }
34         catch(InterruptedException e){}
35     }
36 }*/
37 class BounceFrame extends JFrame{
38     private BallComponent comp;
39     public static final int STEPS=1000;
40     public static final int DELAY=100;
41     public BounceFrame(){
42         comp=new BallComponent();
43         add(comp,BorderLayout.CENTER);
44         JPanel buttonPanel=new JPanel();
45         addButton(buttonPanel,"Start",new ActionListener(){
46             public void actionPerformed(ActionEvent event){
47                 addBall();
48             }
49         });
50         addButton(buttonPanel,"Close",new ActionListener(){
51             public void actionPerformed(ActionEvent event){
52                 System.exit(0);
53             }
54         });
55         add(buttonPanel,BorderLayout.SOUTH);
56         pack();
57     }
58     public void addButton(Container c,String title,ActionListener listener){
59         JButton button=new JButton(title);
60         c.add(button);
61         button.addActionListener(listener);
62     }
63     /*public void addBall(){
64         Ball b=new Ball();
65         comp.add(b);
66         Runnable r=new BallRunnable(b,comp);
67         Thread t=new Thread(r);
68         t.start();
69     }*/
70     public void addBall(){
71         try{
72             Ball ball=new Ball();
73             comp.add(ball);
74             for(int i=1;i<=STEPS;i++){
75                 ball.move(comp.getBounds());
76                 comp.paint(comp.getGraphics());
77                 Thread.sleep(DELAY);
78             }
79         }
80         catch(InterruptedException e){}
81     }
82 }
View Code

  BollComponent.java

 1 package Thread;
 2 import java.awt.*;
 3 
 4 import java.util.*;
 5 import javax.swing.*;
 6 public class BallComponent extends JPanel{
 7     private static final int DEFAULT_WIDTH=450;
 8     private static final int DEFAULT_HEIGHT=350;
 9     private java.util.List<Ball>balls=new ArrayList<>();
10     public void add(Ball b){
11         balls.add(b);
12     }
13     public void paintComponent(Graphics g){
14         super.paintComponent(g);
15         Graphics2D g2=(Graphics2D)g;
16         for(Ball b:balls){
17             g2.fill(b.getShape());
18         }
19     }
20     public Dimension getPreferredSize(){
21         return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
22     }
23 }
View Code

  Ball.java

 1 package Thread;
 2 import java.awt.geom.*;
 3 import java.awt.geom.Ellipse2D.Double;
 4 public class Ball {
 5     private static final int XSIZE=15;
 6     private static final int YSIZE=15;
 7     private double x=0;
 8     private double y=0;
 9     private double dx=1;
10     private double dy=1;
11     public void move(Rectangle2D bounds){
12         x+=dx;
13         y+=dy;
14         if(x<bounds.getMinX()){
15             x=bounds.getMinX();
16             dx=-dx;
17         }
18         if(x+XSIZE>=bounds.getMaxX()){
19             x=bounds.getMaxX()-XSIZE;
20             dx=-dx;
21         }
22         if(y<bounds.getMinY()){
23             y=bounds.getMinY();
24             dy=-dy;
25         }
26         if(y+YSIZE>=bounds.getMaxY()){
27             y=bounds.getMaxY()-YSIZE;
28             dy=-dy;
29         }
30     }
31     public Ellipse2D getShape(){
32         return new Ellipse2D.Double(x,y,XSIZE,YSIZE);
33     }
34 }
View Code

  针对上述的情况,下面的代码是改进后的,当点击close时,就会退出当前线程。而且不论何时点击Start按钮,addBall都会启动一个新线程.

  实现多个线程的方法:将移动球的代码放置在一个独立的线程中,点击开始就会重新启动一个线程。简单过程如下:

    1、将任务代码放在实现了Runnable接口的类的run方法中。

1 class MyRunnable implements Runnable{
2      public void run(){
3          task code
4     }   
5 }

    2、创建一个类对象。Runnable r=new MyRunnable();

    3、由Runnable创建一个Thread对象。Thread t=new Thread();

    4、启动线程:t.start();

 BounceThread.java

 1 package Thread;
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import javax.swing.*;
 5 public class BounceThread {
 6     public static void main(String[] args){
 7         EventQueue.invokeLater(new Runnable(){
 8             public void run(){
 9                 JFrame frame=new BounceFrame();
10                 frame.setTitle("BounceFrame");
11                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12                 frame.setVisible(true);
13             }
14         });
15     }
16 }
17 class BallRunnable implements Runnable{
18     private Ball ball;
19     private Component component;
20     public static final int STEPS=1000;
21     public static final int DELAY=5;
22     public BallRunnable(Ball aBall,Component aComponent){
23         ball=aBall;
24         component=aComponent;
25     }
26     public void run(){
27         try{
28             for(int i=1;i<=STEPS;i++){
29                 ball.move(component.getBounds());
30                 component.repaint();
31                 Thread.sleep(DELAY);
32             }
33         }
34         catch(InterruptedException e){}
35     }
36 }
37 class BounceFrame extends JFrame{
38     private BallComponent comp;
39     //public static final int STEPS=1000;
40     //public static final int DELAY=100;
41     public BounceFrame(){
42         comp=new BallComponent();
43         add(comp,BorderLayout.CENTER);
44         JPanel buttonPanel=new JPanel();
45         addButton(buttonPanel,"Start",new ActionListener(){
46             public void actionPerformed(ActionEvent event){
47                 addBall();
48             }
49         });
50         addButton(buttonPanel,"Close",new ActionListener(){
51             public void actionPerformed(ActionEvent event){
52                 System.exit(0);
53             }
54         });
55         add(buttonPanel,BorderLayout.SOUTH);
56         pack();
57     }
58     public void addButton(Container c,String title,ActionListener listener){
59         JButton button=new JButton(title);
60         c.add(button);
61         button.addActionListener(listener);
62     }
63     public void addBall(){
64         Ball b=new Ball();
65         comp.add(b);
66         Runnable r=new BallRunnable(b,comp);
67         Thread t=new Thread(r);
68         t.start();
69     }
70     /*public void addBall(){
71         try{
72             Ball ball=new Ball();
73             comp.add(ball);
74             for(int i=1;i<=STEPS;i++){
75                 ball.move(comp.getBounds());
76                 comp.paint(comp.getGraphics());
77                 Thread.sleep(DELAY);
78             }
79         }
80         catch(InterruptedException e){}
81     }*/
82 }
View Code

 BollComponent.java

 1 package Thread;
 2 import java.awt.*;
 3 
 4 import java.util.*;
 5 import javax.swing.*;
 6 public class BallComponent extends JPanel{
 7     private static final int DEFAULT_WIDTH=450;
 8     private static final int DEFAULT_HEIGHT=350;
 9     private java.util.List<Ball>balls=new ArrayList<>();
10     public void add(Ball b){
11         balls.add(b);
12     }
13     public void paintComponent(Graphics g){
14         super.paintComponent(g);
15         Graphics2D g2=(Graphics2D)g;
16         for(Ball b:balls){
17             g2.fill(b.getShape());
18         }
19     }
20     public Dimension getPreferredSize(){
21         return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
22     }
23 }
View Code

  Ball.java 

 1 package Thread;
 2 import java.awt.geom.*;
 3 import java.awt.geom.Ellipse2D.Double;
 4 public class Ball {
 5     private static final int XSIZE=15;
 6     private static final int YSIZE=15;
 7     private double x=0;
 8     private double y=0;
 9     private double dx=1;
10     private double dy=1;
11     public void move(Rectangle2D bounds){
12         x+=dx;
13         y+=dy;
14         if(x<bounds.getMinX()){
15             x=bounds.getMinX();
16             dx=-dx;
17         }
18         if(x+XSIZE>=bounds.getMaxX()){
19             x=bounds.getMaxX()-XSIZE;
20             dx=-dx;
21         }
22         if(y<bounds.getMinY()){
23             y=bounds.getMinY();
24             dy=-dy;
25         }
26         if(y+YSIZE>=bounds.getMaxY()){
27             y=bounds.getMaxY()-YSIZE;
28             dy=-dy;
29         }
30     }
31     public Ellipse2D getShape(){
32         return new Ellipse2D.Double(x,y,XSIZE,YSIZE);
33     }
34 }
View Code

运行结果如下: 

 

当神已无能为力,那便是魔渡众生
目录
相关文章
|
4天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
45 14
|
7天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
37 13
|
8天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
1月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
109 17
|
2月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
2月前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
2月前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
2月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
77 3
|
2月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
231 2

热门文章

最新文章