标签:并发 系统时间 tst 常用 需要 基本概念 技术 ++ 场景
虽然在学习的时候学过多线程编程,但是公司的项目一直没有采用多线程编程,自己对于多线程编程的理解已仅仅的是停留在了最初的学习阶段,
因为最近项目中用到了下载功能,采用单线程下载感觉很慢,所以自己就打算在研究一下多线程,以提高下载的速率。
参考文档:https://www.cnblogs.com/wxd0108/p/5479442.html
在使用线程之前应该先了解其概念,“什么是线程?”,“有什么用?”,只有带着问题去学习,你才能更快更好的去学会它(“怎么用?”)。
进程:进程是操作系统中运行的一个任务,一个应用程序运行在一个进程中。进程是一块包含了某些资源的内存区域。操作系统利用进程把它的
工作划分为一些功能单元。进程中所包含的一个或多个执行单元称为线程。
线程:一个线程是进程的一个顺序执行流,一个进程中可以包含多个线程。
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,
那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,
我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。
同步:这里的同步,可以理解为“协同步调”,Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。
我们的程序应该在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
线程的使用场合:
a. 线程通常用于在一个程序中需要同时完成多个任务的情况。我们可以将每个任务定义为一个线程,使他们得以一同工作;
b. 用于在单一线程中可以完成,但是使用多线程可以更快的情况。eg:下载文件。
线程的状态:
创建线程有两种方式
方式一:继承Thread并重写run方法。
Thread类是线程类,它的每一个实例都表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。
重写run方法的目的只要是为了让线程执行我们所需要的逻辑。
class MyThread1 extends Thread{ public void run(){ for(int i=0;i<1000;i++){ System.out.println("你是谁啊?"); } } } Thread t1 = new MyThread1();
启动线程要调用start方法,不要直接调用线程的run()方法当线程启动后,run方法会自动被执行。
线程启动后,何时运行,运行多久都听线程调度管理。线程对于线程调度的工作是不可控的。
即:
线程何时被分配CPU时间不可控,分配多久时间不可控。线程不能主动向线程调度要时间只能被动被分配。
线程调度会尽可能将CPU时间均匀的分配给所有线程,但不保证一个线程一次这样规律的切换。
此种创建线程的方式有两个不足:
a. 由于需要继承Thread,而java又是单继承原则,这就导致当前类不能在继承其他类,很多时候会在实践开发中出现继承冲突问题。
b. 由于在线程内部重写run方法定义了当前线程要执行的任务,这就导致了线程与任务有一个强耦合关系,不利于线程重用。
第二种创建线程的方式:单独定义线程任务,实现Runnable接口。
实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
public class ThreadDemo2 { public static void main(String[] args) { Runnable r1=new MyRunnable1(); Runnable r2=new MyRunnable2(); Thread t1=new Thread(r1); Thread t2=new Thread(r2); t1.start(); t2.start(); } } class MyRunnable1 implements Runnable{ public void run() { for(int i=0;i<1000;i++){ System.out.println("你最喜欢的人是谁?"); } } } class MyRunnable2 implements Runnable{ public void run(){ for(int i=0;i<1000;i++){ System.out.println("我最喜欢的人是xxx!"); } } }
优点:
a. 可以将线程与线程中我们所要执行的业务逻辑分离开来,减少耦合;
b. 解决了java中单继承的问题,因为接口是多继承的。
方式三:使用匿名内部类完成以上两种线程的创建方式。
使用该方式可以简化编写代码的复杂度,当一个线程仅需要一个实例时,我们可以通过此方式创建。
//方式一 new Thread(){ public void run(){ for(int i=0;i<1000;i++){ System.out.println("你爱我么?"); } } }.start(); //方式二 new Thread(new Runnable(){ public void run(){ for(int i=0;i<1000;i++){ System.out.println("我爱你"); } } }).start();
2.1 线程优先级
线程优先级有10个等级,分别用数字1-10表示其中1最低,10最高,5为默认值。
理论上,优先级越高的线程,获取CPU时间片的次数多。
Thread max=new Thread(){ public void run(){ for(int i=0;i<10000;i++){ System.out.println("max"); } } }; Thread min=new Thread(){ public void run(){ for(int i=0;i<10000;i++){ System.out.println("min"); } } }; Thread norm=new Thread(){ public void run(){ for(int i=0;i<10000;i++){ System.out.println("norm"); } } }; max.setPriority(Thread.MAX_PRIORITY); min.setPriority(Thread.MIN_PRIORITY); min.start(); norm.start(); max.start();
2.2 线程阻塞sleep()
线程提供了静态方法:static void sleep(long ms),该方法会将调用该方法的线程阻塞指定毫秒,当阻塞超时后,
线程会自动回到runnable状态等待分配CPU 时间片继续并发运行。
/* * 电子表 * 每秒钟输出一次当前系统时间 * 格式:HH:mm:ss * 例如:09:53:32 */ SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss"); while(true){ try{ System.out.println(sdf.format(new Date())); Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } }
3.获取线程相关信息的一系列方法
//获取运行当前代码片段的线程 Thread t=Thread.currentThread(); //返回该线程的标识符 long id=t.getId(); //返回该线程的名称 String name=t.getName(); //返回该线程的优先级 int priority=t.getPriority(); //测试线程是否处于活动状态 boolean isAlive=t.isAlive(); //测试线程是否为守护线程 boolean isDaemon=t.isDaemon(); //测试线程是否已经中断 boolean isInterrupted=t.isInterrupted();
标签:并发 系统时间 tst 常用 需要 基本概念 技术 ++ 场景
原文地址:https://www.cnblogs.com/shiyun32/p/9308957.html