通过上一篇 多线程并发 (一) 了解 Java 虚拟机 - JVM 了解了java 虚拟机的构成以及对象的创建等。从Java虚拟机栈我们知道每当我们创建一个线程JVM就会给我们的线程分配一个私有的内存空间和程序计数器记录当前线程运行的次行代码的地址。了解了Thread之后,通过多线程编程进而引入锁(下篇)的概念。
1. 线程简介

线程是进程中可独立执行的最小单位,也是 CPU 资源分配的基本单位。

2. 线程的四个属性(1)编号Id
              线程的编号(id)用于标识不同的线程,每条线程拥有不同的编号。
    (2) 名字
             每个线程都有自己的名字(name),名字的默认值是 Thread-线程编号,比如 Thread-0 。除了默认值,我们也可以给线程设置名字,以我们自己的方式去区分每一条线程。
    (3)类别
            分为守护线程和用户线程,我们可以通过 setDaemon(true) 把线程设置为守护线程。GC线程是守护线程,对于守护线程JVM退出时不会考虑次线程是否持行完毕。
    (4)优先级
            线程的优先级(Priority)用于表示应用希望优先运行哪个线程,线程调度器会根据这个值来决定优先运行哪个线程。
            Java 中线程优先级的取值范围为 1~10,默认值是 5,Thread 中定义了下面三个优先级常量。
            最低优先级:MIN_PRIORITY = 1
            默认优先级:NORM_PRIORITY = 5
            最高优先级:MAX_PRIORITY = 10
3. 线程的六个方法

线程的常用方法有六个,它们分别是三个非静态方法 start()、run()、join() 和三个静态方法 currentThread()、yield()、sleep() 。我们只关注start()、join()、yield()、sleep(),另外run()、currentThread()容易理解。
(1)yield() 
      顾名思义yield英文 让 的意思。当t.yield() 持行之后 t 回到就绪状态,所有线程重新抢用cpu。
(2)join() 
      t2正在运行,这时在t2线程中调用 t1.join() ,这是让t2 等待线程t1 结束了,再运行。次方法可以让线程按顺序持行。自己线程中调用自己的join方法是无意义的。
(3)sleep()
      暂停阻塞等待一段时间,时间过了就继续。这个方法是不释放锁的。
(4)start()
     启动线程,让cpu调度器调度开启线程持行run()方法。而直接调用run()方法只是运行当前run方法,不会开启线程。

4.线程运行状态

线程调用状态图

分析:1. new 的状态(NEW)
           2. start() 之后线程到达就绪状态等待cpu调度,cpu调度了 就是运行状态。(RUNNABLE)
           3. 调用了 TImedWaiting 就回到就绪状态,结束之后自动到运行状态(TIMED_WAITING)
           4. 调用 Waiting 就绪然后持行相应的方法到运行(WAITING)
           5. 调用 Blocked 等待进入同步代码快的锁。(BLOCKED)
           6. 结束状态Teminated(TERMINATED)
线程被挂起:正在cpu中调度的线程,cpu把该线程丢出去,持行其他线程,这就是线程被挂起。之后又回来持续该线程。(cpu每次只能持行一个线程)
5.Java 线程调度原理

在任意时刻,CPU 只能执行一条机器指令,每个线程只有获取到 CPU 的使用权后,才可以执行指令。
也就是在任意时刻,只有一个线程占用 CPU,处于运行的状态。
多线程并发运行实际上是指多个线程轮流获取 CPU 使用权,分别执行各自的任务。
线程的调度由 JVM 负责,线程的调度是按照特定的机制为多个线程分配 CPU 的使用权。
线程调度模型分为两类:分时调度模型和抢占式调度模型。

1.分时调度模型
分时调度模型是让所有线程轮流获取 CPU 使用权,并且平均分配每个线程占用 CPU 的时间片。
2.抢占式调度模型
JVM 采用的是抢占式调度模型,也就是先让优先级高的线程占用 CPU,如果线程的优先级都一样,那就随机选择一个线程,并让该线程占用 CPU。
也就是如果我们同时启动多个线程,并不能保证它们能轮流获取到均等的时间片。
如果我们的程序想干预线程的调度过程,最简单的办法就是给每个线程设定一个优先级。
6.线程创建的四种方式
    1)继承Thread类创建线程
    2)实现Runnable接口创建线程
    3)使用Callable和Future创建线程
    4)使用线程池例如用Executor框架
具体代码也是非常简单,仅仅贴出Future的代码:

[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class MyTask  implements Callable<Object>{   
    private String args1;
    private String args2;
    //构造函数,用来向task中传递任务的参数
    public  MyTask(String args1,String args2) {
        this.args1=args1;
        this.args2=args2;
    }
    //任务执行的动作
    @Override
    public Object call() throws Exception {
         
        for(int i=0;i<100;i++){
            System.out.println(args1+args2+i);
        }
        return true;
}
[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
MyTask myTask = new MyTask("www", "www");
FutureTask<Object> futureTask = new FutureTask<>(myTask);
Thread thread = new Thread(futureTask);
thread.start();
//get()阻塞等待直到任务执行完成。
boolean result = (boolean) futureTask.get();

7.LockSupport
我们可以使用它来阻塞和唤醒线程,功能和wait,notify有些相似,但是LockSupport比起wait,notify功能更强大,也好用的多。
1)对比wait,notify
wait和notify都是Object中的方法,在调用这两个方法前必须先获得锁对象。
当对象的等待队列中有多个线程时,notify只能随机选择一个线程唤醒,无法唤醒指定的线程。
LockSupport的话,我们可以在任何场合使线程阻塞,同时也可以指定要唤醒的线程。

[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class LockSupportTest {
  
    public static void main(String[] args) {
        Thread parkThread = new Thread(new ParkThread());
        parkThread.start();
        System.out.println("开始线程唤醒");
        LockSupport.unpark(parkThread);
        System.out.println("结束线程唤醒");
  
    }
  
    static class ParkThread implements Runnable{
  
        @Override
        public void run() {
            System.out.println("开始线程阻塞");
            LockSupport.park();
            System.out.println("结束线程阻塞");
        }
    }
}

可指定线程阻塞和唤醒。摘学于:https://www.cnblogs.com/takumicx/p/9328459.html
8.CountDownLatch
CountDownLatch开启一组线程,让每个线程去持行自己的任务,等所有线程都持行完毕后,再向下持行代码。比如我们开启一组线程去同步服务器的数据有 播放记录,下载记录,收藏记录等等用户信息。

[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
CountDownLatch latch = new CountDownLatch(5);
        for (int i =0;i<5;i++){
            new Thread(new MyTask(latch,i*1000,i)).start();
        }
        try {
            latch.await();
            Log.e("WXY--","All the tasks are over.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
[AppleScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class MyTask implements Runnable {
    CountDownLatch latch;
    int delayTime; // Test
    int curThread;
    public MyTask(CountDownLatch latch,int delayTime,int curThread) {
        this.latch = latch;
        this.delayTime = delayTime;
        this.curThread = curThread;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(delayTime);
            Log.e("WXY--","current task = " + curThread);
            latch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这篇相对简单主要是认识线程,了解线程的方法。介绍了两个线程同步的类。

更多技术资讯可关注:itheimaGZ获取

多线程并发 了解 Thread的更多相关文章

  1. Java面试题整理一(侧重多线程并发)

    1..是否可以在static环境中访问非static变量? 答:static变量在Java中是属于类的,它在所有的实例中的值是一样的.当类被Java虚拟机载入的时候,会对static变量进行初始化.如 ...

  2. HashMap多线程并发问题分析

    转载: HashMap多线程并发问题分析 并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题. ...

  3. 用读写锁三句代码解决多线程并发写入文件 z

    C#使用读写锁三句代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题 在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三 ...

  4. 由获取微信access_token引出的Java多线程并发问题

    背景: access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.access_token的存储至少要保留512个字符空间.acces ...

  5. ThreadLocal 多线程并发,数据隔离

    ThreadLocal:  创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...

  6. Cocos2d-x优化中多线程并发访问

    多线程并发访问在Cocos2d-x引擎中用的不是很多,这主要是因为中整个结构设计没有采用多线程.源自于Objective-C的Ref对象,需要使用AutoreleasePool进行内存管理,Autor ...

  7. 多线程并发 synchronized对象锁的控制与优化

    本文针对用户取款时多线程并发情境,进行相关多线程控制与优化的描述. 首先建立用户类UserTest.业务操作类SynchronizedTest.数据存取类DataStore,多线程测试类MultiTh ...

  8. Cocos2d-x优化中多线程并发訪问

    多线程并发訪问在Cocos2d-x引擎中用的不是非常多,这主要是由于中整个结构设计没有採用多线程. 源自于Objective-C的Ref对象,须要使用AutoreleasePool进行内存管理,Aut ...

  9. 多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture

    目录 1.Futrue 2.FutureTask 3.CompletionService 4.CompletableFuture 5.总结 ================正文分割线========= ...

随机推荐

  1. echarts图表重设尺寸

    在绘制chart的方法中添加下面语句,则会在尺寸变化的时候,重新绘制图表 window.addEventListener("resize", function () { myCha ...

  2. 吴裕雄--天生自然 PHP开发学习:MySQL 插入多条数据

    <?php $servername = "localhost"; $username = "root"; $password = "admin& ...

  3. Ubuntu--- 安装VMware 报错 Build enviroment error!

    今天从 Ubuntu 安装 VMware 下载并安装过程都很顺利,但是在启动过程中报错误,所以总结如下: 报错原因:VMware 第一次启动需要编译一些模块,但是刚开始并没有安装 gcc 所以便报无法 ...

  4. android studio 修改新建EmptyActivity默认布局

    https://www.jianshu.com/p/d4f201135097 打开你的Android Sudio安装目录,我的为D:\Program Files\Android\Android Stu ...

  5. uboot对Flash和DDR的分区管理

    1.uboot阶段对Flash的分区 (1).所谓分区,就是对Flash进行分块管理. (2).PC机等产品中,因为大家都是在操作系统下使用硬盘的,整个硬盘由操作系统统一管理,操作系统会使用文件系统帮 ...

  6. Python笔记_第四篇_高阶编程_GUI编程之Tkinter_6.附录

    1. 事件附录: 2. 事件属性附录:

  7. dubbo的启动时检查

    修改的消费者(xml) 修改的消费者(注解)

  8. Using sudo inside a docker container

    https://stackoverflow.com/questions/25845538/using-sudo-inside-a-docker-container FROM ubuntu:12.04 ...

  9. [转]Apache漏洞利用与安全加固实例分析

    1.2 Apache文件解析特性 Apache对于文件名的解析是从后往前解析的,直到遇见一个它认识的文件类型为止.因此,如果web目录下存在以类似webshell.php.test这样格式命名的文件, ...

  10. SQLServer 安装提示需要重启计算机的解决方案

    处理方法:在开始-程序-运行中(或者直接windows+R件同时),输入regedit回车,在注册表中找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Con ...