【线程管理】之篇一

简介:

一、简介

并发(Concurrency)指的是一系列任务的同时运行。如果一台电脑多个处理器或者多核处理器,这个同时性是真正意义上的并发;但一电脑只有一个单核处理器,这个同时性并不是真正的并发。

线程thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
举个例子:在唱歌的时候,你可以同时阅读电子邮件,又可以浏览网页。这种存在着 进程级(Process-Level)并发。在浏览网页中,可以看书,视频,写日志,写博…有着多个同时进行的任务,这种进程并发任务为 线程(Thread)。

二、简单介绍线程创建和运行

java程序创建线程有两种方法:

  • 继承Thread类,覆盖run方法。
  • 创建一个Runnable接口的类。使用带参数的Thread构造器创建Thread对象。这个参数就是实现Runnable接口的类的一个对象。

用第二种方式,举个很简单的例子:Calculator.java

public class Calculator implements Runnable
{

    private int number;

    public Calculator(int number)
    {
        this.number = number;
    }

    @Override
    public void run()
    {
        for (int i = 0; i <= 10; i++)
        {
            System.out.printf("%s: %d * %d = %d \n", Thread.currentThread()
                    .getName(), number, i, i * number);
        }
    }

    public static void main(String[] args)
    {
        new Thread(new Calculator(1)).start();;
    }
}

你可以看到以下输出:

Thread-0: 1 * 0 = 0 
Thread-0: 1 * 1 = 1 
Thread-0: 1 * 2 = 2 
Thread-0: 1 * 3 = 3 
Thread-0: 1 * 4 = 4 
...

Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

  • 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
  • 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

注意:其实  java.lang.Thread 实现的接口:Runnable 。只有当调用Thread对象的start方法,新的线程才会被创建。

 

相关资料:

java.lang.Thread

Thread(Runnable target)
          分配新的 Thread 对象。

void start()
          使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

static Thread currentThread()
          返回对当前正在执行的线程对象的引用。

String getName()
          返回该线程的名称。

三、线程信息的获取和设置

Thread类有了一些保存信息的属性,这些属性用来标识线程,显示线程状态或控制线程优先级。

  • ID: 保存了线程唯一标识符
  • Name: 线程名称
  • Priority: 线程的优先级。 1-10 最低优先级为1 最高优先级为10.
  • Status线程状态。线程可以处于下列状态之一:

           NEW
           至今尚未启动的线程处于这种状态。

           RUNNABLE
           正在 Java 虚拟机中执行的线程处于这种状态。

           BLOCKED
           受阻塞并等待某个监视器锁的线程处于这种状态。

           WAITING
          无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。

          TIMED_WAITING
          等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。

          TERMINATED
          已退出的线程处于这种状态。

下面演示一个 10个线程 名称和优先级 输出他们的状态信息直到线程结束:

Calculator.java

public class Calculator implements Runnable
{

    private int number;
    public Calculator(int number)
    {
        this.number = number;
    }
    @Override
    public void run()
    {
        for (int i = 0; i <= 10 ; i++)
        {
            System.out.printf("%s: %d * %d = %d \n", Thread.currentThread().getName(),number,i,i*number);
        }
    }

}

测试代码:Main.java

public class Main
{

    public static void main(String[] args)
    {
        Thread threads[] = new Thread[10];
        Thread.State status[] = new Thread.State[10];
        
        for (int i = 0; i < 10; i++)
        {
            threads[i] = new Thread(new Calculator(i));
            
            if ((i%2) == 0)
                threads[i].setPriority(Thread.MAX_PRIORITY);
            else 
                threads[i].setPriority(Thread.MIN_PRIORITY);
            
            threads[i].setName("Thread "+i);
        }
        
        try(FileWriter file = new FileWriter(".\\log.txt");
                PrintWriter pw = new PrintWriter(file);)
        {
            for (int i = 0; i < 10; i++)
            {
                pw.println("Main: status of Thread "+i+" : "+threads[i].getState());
                status[i] = threads[i].getState();
            }
            
            for (int i = 0; i < 10; i++)
            {
                threads[i].start();
            }
            
            boolean finish = false;
            while (!finish)
            {
                for (int i = 0; i < 10; i++)
                {
                    if (threads[i].getState() != status[i])
                    {
                        writeThreadInfo(pw , threads[i],status[i]);
                        status[i] = threads[i].getState();
                    }
                }
                finish = true;
                for (int i = 0; i < 10; i++)
                {
                    finish = finish && (threads[i].getState() == State.TERMINATED);
                }
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private static void writeThreadInfo(PrintWriter pw, Thread thread,
            State state)
    {
        pw.printf("Main: id %d - %s \n", thread.getId(),thread.getName());
        pw.printf("Main: Priority: %d\n", thread.getPriority());
        pw.printf("Main: Old state: %s \n", state);
        pw.printf("Main: new state: %s \n", thread.getState());
        pw.printf("Main: *******************************\n");
    }

}

你可以看到控制台 和 生成的log.txt记录者每个线程的状态演变。最高的比最低优先级的线程结束的早。

imageimage

注意:如果没有给线程置顶名字,JVM给分配默认格式名字:Thread-XX(XX为一组数字).同样线程ID和状态是不许改变的。线程类没提供setId() 和 setStatus()。

相关资料:

java.lang.Thread

void setPriority(int newPriority)
          更改线程的优先级。

void setName(String name)
          改变线程名称,使之与参数 name 相同。

Thread.State getState()
          返回该线程的状态。

四、线程中断:interrupt() 或者 使用java异常控制

线程中,java提供了中断机制。但是如果是复杂的算法 和 分布在几个方法中,java提供了InterruptedException一次控制线程中断。

下面各自举个例子:

第一个创建一个线程,运行5秒钟后再通过中断机制强制使其终止。

PrimeGenerator .java

public class PrimeGenerator extends Thread
{
    @Override
    public void run()
    {
        long number = 1L;
        while (true)
        {
            if(isPrime(number))
                System.out.println("Number "+number+" is Prime");
            
            if (isInterrupted())
            {
                System.out.println("The Prime Generator has been Interrupted");
                return ;
            }
            number++;
        }
    }

    private boolean isPrime(long number)
    {
        if (number < 2)
            return true;
        for (long i = 2; i < number; i++)
        {
            if ((number % i) == 0)
                return false;
        }
        
        return true;
    }
    
}

测试代码:Main.java

public class Main
{

    public static void main(String[] args)
    {
        Thread task = new PrimeGenerator();
        task.start();
        try
        {
            Thread.sleep(5000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        task.interrupt();
    }
}

你可以观察到,5秒钟内任务在跳动,但是

                        image

相关资料:

java.lang.Thread

boolean isInterrupted()
          测试线程是否已经中断。

static void sleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

void interrupt()
          中断线程。

 

第二个我们用InterruptedException异常来控制线程中断。

FileSearch.java

public class FileSearch implements Runnable
{

    private String initPath;
    private String fileName;
    
    public FileSearch(String initPath, String fileName)
    {
        this.initPath = initPath;
        this.fileName = fileName;
    }

    @Override
    public void run()
    {
        File file = new File(initPath);
        if (file.isDirectory())
        {
            try
            {
                directoryProcess(file);
            } catch (InterruptedException e)
            {
                System.out.printf("%s: The search has been interupted",Thread.currentThread().getName());
            }
        }
    }

    private void directoryProcess(File file) throws InterruptedException 
    {
        File list[]  = file.listFiles();
        if (list != null)
        {
            for (int i = 0; i < list.length; i++)
            {
                if (list[i].isDirectory())
                {
                    directoryProcess(list[i]);    
                }
                else {
                    fileProcess(list[i]);
                }
            }
        }
        if (Thread.interrupted())
        {
            throw new InterruptedException();
        }
    }
    
    private void fileProcess(File file) throws InterruptedException
    {
        if (file.getName().equals(fileName))
        {
            System.out.println( Thread.currentThread().getName()+" : "+file.getAbsolutePath());
        }
        if (Thread.interrupted())
        {
            throw new InterruptedException();
        }
    }

}

测试代码:Main.java

package sedion.jeffli.concurrency4;

import java.util.concurrent.TimeUnit;

public class Main
{

    public static void main(String[] args)
    {
        FileSearch fileSearch = new FileSearch("C:\\", "abc.bat");
        Thread thread = new Thread(fileSearch);
        thread.start();
        
        try
        {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

不管地用了多少次,只要线程检测到它已经中断了,立即抛出InterruptedException,继续赤星run方法。你可以看到,当输出:
                  image

五、线程的休眠和恢复

有时候你需要在某一个预期时间中中断线程的执行。例如像每隔一分钟检查…我们可以调用线程的sleep()方法,也可以用TimeUtil美剧累元素进行调用。

下面使用sleep()方法,每隔一秒钟就输出实际时间。

FileClock.java

package sedion.jeffli.concurrency5;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class FileClock implements Runnable
{

    @Override
    public void run()
    {
        for (int i = 0; i < 10; i++)
        {
            System.out.println(new Date());
            try
            {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e)
            {
                System.out.println("The File Clock has been interrupted");
            }
        }
    }
    
}

测试代码:FileMain.java

package sedion.jeffli.concurrency5;

import java.util.concurrent.TimeUnit;

public class FileMain
{
    public static void main(String[] args)
    {
        FileClock fileClock = new FileClock();
        Thread thread = new Thread(fileClock);
        thread.start();
        
        try
        {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

你会看到如下的展示:
                  image

最佳实践:当线程被中断时,释放或者关闭线程正在使用的资源。

六、等待线程的终止


使用线程来完成这些初始化任务,等待线程终止,执行程序的其他任务。为了达到这个目的,我们可以用Thread类的join()方法。当一个线程对象的join方法被调用时,调用它的线程被挂起,知道这个线程对象完成他的任务。

例如 一种初始化资源 join()方法

DataSourcesLoader.java  NetworkConnectionsLoader.java

public class DataSourcesLoader implements Runnable
{

    @Override
    public void run()
    {
        System.out.println("Beginning data sources loading: "+new Date());
        try
        {
            TimeUnit.SECONDS.sleep(4);    
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("Beginning data sources loading has finished: "+new Date());
    }

}
public class NetworkConnectionsLoader implements Runnable
{

    @Override
    public void run()
    {
        System.out.println("Beginning data sources loading: "+new Date());
        try
        {
            TimeUnit.SECONDS.sleep(6);    
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("Beginning data sources loading has finished: "+new Date());
    }

}

测试代码:

public class Main
{
    public static void main(String[] args)
    {
        DataSourcesLoader dataSourcesLoader = new DataSourcesLoader();
        Thread thread1 = new Thread(dataSourcesLoader,"DataSourcesLoader");
        
        NetworkConnectionsLoader connectionsLoader = new NetworkConnectionsLoader();
        Thread thread2 = new Thread(connectionsLoader,"NetworkConnectionsLoader");
    
        thread1.start();
        thread2.start();
        
        try
        {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        
        System.out.println("Main: has finished :"+new Date());
    }
}

这个代码你可以看到两个线程对象是如何运行的。DataSourcesLoader运行结束,直到NetworkConnectionsLoader也结束的时候,主程序的对象继续运行并打出最终的信息。

          image

相关资料:

java.lang.Thread java.util.concurrent.TimeUnit

public final void join()
                throws InterruptedException
等待该线程终止。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的 中断状态 被清除。

void sleep(long timeout)
          使用此单元执行 Thread.sleep.这是将时间参数转换为 Thread.sleep 方法所需格式的便捷方法。

相关文章
|
3天前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
|
6月前
|
并行计算 安全 Java
探索多线程编程:守护线程与线程生命周期
探索多线程编程:守护线程与线程生命周期
31 0
|
9月前
|
缓存 前端开发 Java
线程池中线程重用导致的问题
之前在公司做的一个项目中,有一个 core 的公共依赖包,那个依赖里面简单封装了用户的信息。
|
消息中间件 Java 关系型数据库
多线程之线程管理
多线程之线程管理
|
开发框架 Java .NET
C#多线程开发-线程池03
C#多线程开发-线程池03
111 0
|
缓存 Java API
多线程编程之线程池的使用
前面的几篇文章主要介绍了java中多线程编程中线程的一些概念。比如线程编程中一些常用的API,以及线程的暂停的恢复还有线程的终止。这些都是多线程编程的入门的基础,涉及到的都是单个线程使用,今天这篇文章就来说说在多线程编程中线程池的概念。
150 0
多线程编程之线程池的使用
|
Java
Java线程等待、唤醒通信机制详解(上)
Java线程等待、唤醒通信机制详解(上)
127 0
Java线程等待、唤醒通信机制详解(上)

热门文章

最新文章