java -- 线程

打印 上一主题 下一主题

主题 935|帖子 935|积分 2807

线程与进程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
进程与线程的区别

进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

  • 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
  • Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
  • 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
线程的创建

继承Thread类

Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。
Java中通过继承Thread类来创建启动多线程的步骤如下:

  • 定义一个类继承Thread类
  • 重写run 方法(线程任务)
  • 开启线程
    创建子类对象
    调用thread类的start方法
  1. class MyThread extends Thread {
  2.         @Override
  3.         public void run() {
  4.                 for (int i = 0; i < 50; i++) {
  5.                         System.out.println("sub... "+i);
  6.                 }
  7.         }
  8. }
  9. public static void main(String[] args) {
  10.     // 创建自定义的线程对象
  11.     MyThread mt = new MyThread();
  12.     // 开启线程
  13.     mt.start();
  14.         // 一个线程仅可以被启动一次
  15.         // mt.start()
  16.     // 主线程执行
  17.     for (int i = 0; i < 50; i++) {
  18.             System.out.println("main... "+i);
  19.     }
  20. }
复制代码
线程名字的设置和获取


  • Thread类的方法String getName()可以获取到线程的名字。
  • Thread类的方法setName(String name)设置线程的名字。
  • 通过Thread类的构造方法Thread(String name)也可以设置线程的名字。
  1. public class Demo {
  2.     public static void main(String[] args) {
  3.         MyThread mt = new MyThread();
  4.         //设置线程名字
  5.         mt.setName("旺财");
  6.         mt.start();
  7.     }
  8. }
  9. class MyThread  extends Thread{
  10.     public void run(){
  11.         System.out.println("线程名字:"+super.getName());
  12.     }
  13. }
复制代码
线程是有默认名字的,如果不设置,JVM会赋予线程默认名字Thread-0,Thread-1。
获取运行main方法线程的名字

Demo类不是Thread的子类,因此不能使用getName()方法获取。
Thread类定义了静态方法static Thread currentThread()获取到当前正在执行的线程对象。
  1. public static void main(String[] args){
  2.         Thread t = Thread.currentThread();
  3.         System.out.println(t.getName());
  4. }
复制代码
实现Runnable接口

java.lang.Runnable接口类
步骤如下:

  • 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  • 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  • 调用线程对象的start()方法来启动线程。
  1. public class Demo {
  2.     public static void main(String[] args) {
  3.         // 创建自定义类对象  线程任务对象
  4.         MyRunnable mr = new MyRunnable();
  5.         // 创建线程对象
  6.         Thread t = new Thread(mr);
  7.         t.start();
  8.         for (int i = 0; i < 20; i++) {
  9.             System.out.println("main " + i);
  10.         }
  11.     }
  12. }
  13. public class MyRunnable implements Runnable{
  14.     public void run() {
  15.         for (int i = 0; i < 20; i++) {
  16.         System.out.println(Thread.currentThread().getName()+" "+i);
  17.         }
  18.     }
  19. }
复制代码
通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。
匿名内部类方式创建线程

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
  1. public static void main(String[] args) {
  2.         // 第一种方式 实际上需要的就是Thread类的子类对象
  3.         Thread t = new Thread(){
  4.             @Override
  5.             public void run() {
  6.                 System.out.println("方式一 赋值!");
  7.             }
  8.         };
  9.         t.start();
  10.         new Thread() {
  11.             @Override
  12.             public void run() {
  13.                 System.out.println("方式一 直接调用!");
  14.             }
  15.         }.start();
  16.         // 第二种方式 实际上需要的就是Runnable接口的实现类对象
  17.         Runnable r = new Runnable() {
  18.             @Override
  19.             public void run() {
  20.                 System.out.println("方式二 赋值!");
  21.             }
  22.         };
  23.         new Thread(r).start();
  24.         new Thread(new Runnable() {
  25.             @Override
  26.             public void run() {
  27.                 System.out.println("方式二 直接调用!");
  28.             }
  29.         }).start();
  30.     }
复制代码
Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:

  • 适合多个相同的程序代码的线程去共享同一个资源。
  • 可以避免java中的单继承的局限性。
  • 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
Thread类API

睡眠sleep

public static void  sleep(long time)  让当前线程进入到睡眠状态,到毫秒后自动醒来继续执行
  1. public class Test{
  2.   public static void main(String[] args){
  3.     for(int i = 1;i<=5;i++){
  4.               Thread.sleep(1000);
  5.         System.out.println(i)
  6.     }
  7.   }
  8. }
复制代码
注意:线程运行至同步代码块的时候,需要判断锁,获取锁,出去同步代码块后要释放锁,增加了很多操作,因此线程安全,程序的运行速度慢!
同步方法

同步方法:当一个方法中的所有代码,全部是线程操作的共享数据的时候,可以将整个方法进行同步。使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
  1. public static final int MIN_PRIORITY   //1 最低优先级
  2. public static final int NORM_PRIORITY  //5 默认优先级
  3. public static final int MAX_PRIORITY   //10 最大优先级
复制代码
使用同步方法代码如下:
  1. // 更改线程的优先级
  2. public final void setPriority(int newPriority)
复制代码
同步锁是谁?

  • 对于非static方法,同步锁就是this。
  • 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
Lock锁

java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:

  • public void lock() :加锁。
  • public void unlock():释放锁。
使用如下:
  1.     public static void main(String[] args) {
  2.         Thread t1 = new Thread(new Runnable() {
  3.             @Override
  4.             public void run() {
  5.                 for (int i = 0; i < 50; i++) {
  6.                     System.out.println("t1: " + i);
  7.                 }
  8.             }
  9.         });
  10.         Thread t2 = new Thread(new Runnable() {
  11.             @Override
  12.             public void run() {
  13.                 for (int i = 0; i < 50; i++) {
  14.                     System.out.println("t2: " + i);
  15.                 }
  16.             }
  17.         });
  18.         // 设置优先级
  19.         t1.setPriority(Thread.MAX_PRIORITY);
  20.         t2.setPriority(Thread.MIN_PRIORITY);
  21.         t1.start();
  22.         t2.start();
  23.     }
复制代码
线程状态

这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。
线程状态导致状态发生条件NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。等待和唤醒

Object类的方法
  1. public static void main(String[] args) throws InterruptedException {
  2.         Thread t1 = new Thread(new Runnable() {
  3.             @Override
  4.             public void run() {
  5.                 for (int i = 0; i < 50; i++) {
  6.                     System.out.println("t1: " + i);
  7.                 }
  8.             }
  9.         });
  10.         Thread t2 = new Thread(new Runnable() {
  11.             @Override
  12.             public void run() {
  13.                 for (int i = 0; i < 50; i++) {
  14.                     System.out.println("t2: " + i);
  15.                 }
  16.             }
  17.         });
  18.         t1.start();
  19.         // t1.join();
  20.         /*  t1在此处插队时
  21.             此时 Thread.currentThread() 为 main 线程, 所以 main 线程等待, t1 线程插队
  22.             由于main 线程等待 t2线程还未开启, 因此t1执行完毕, main和t2抢
  23.          */
  24.         t2.start();
  25.         t1.join();
  26.         /*  t1在此处插队时
  27.             此时 Thread.currentThread() 依然为 main 线程, 所以 main 线程等待, t1 线程插队
  28.             而在这之前 t2 线程已经开启, 因此会出现两种情况:
  29.                 1. t2 先执行完 t1 执行完之后 主线程执行
  30.                 2. t1 先执行完 t2 和 主线程抢
  31.          */
  32.         for (int i = 0; i < 10; i++) {
  33.             System.out.println(Thread.currentThread().getName() + " " + i);
  34.         }
  35.     }
复制代码
  1. class JoinMethodTest {
  2.     public static void main(String[] args) throws InterruptedException {
  3.         Thread t1 = new Thread(new Runnable() {
  4.             @Override
  5.             public void run() {
  6.                 for (int i = 0; i < 50; i++) {
  7.                     System.out.println("t1: " + i);
  8.                 }
  9.             }
  10.         });
  11.         Thread t2 = new Thread(new Runnable() {
  12.             @Override
  13.             public void run() {
  14.                 try {
  15.                     t1.join();
  16.                 } catch (InterruptedException e) {
  17.                     e.printStackTrace();
  18.                 }
  19.                 for (int i = 0; i < 50; i++) {
  20.                     System.out.println("t2: " + i);
  21.                 }
  22.             }
  23.         });
  24.         Thread t3 = new Thread(new Runnable() {
  25.             @Override
  26.             public void run() {
  27.                 try {
  28.                     t2.join();
  29.                 } catch (InterruptedException e) {
  30.                     e.printStackTrace();
  31.                 }
  32.                 for (int i = 0; i < 50; i++) {
  33.                     System.out.println("t3: " + i);
  34.                 }
  35.             }
  36.         });
  37.         t1.start();
  38.         t2.start();
  39.         t3.start();
  40.     }
  41. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表