为了9月份的秋招呢,现在开始进行多线程以及并发相关知识的学习。。加油加油

一、一些基本的概念

1.线程和进程

  进程:正在运行的程序,是系统进行资源分配的独立单位。

  线程:进程的执行路径,调度和执行的单位,单个路径单线程,多个路径多线程。

以上的解释太“官方”了,在《Java多线程编程核心技术》里,把进程理解成我们打开的每个程序,而线程则是程序里每一个子任务。

比如,我们打开WeChat.exe运行,此时WeChat.exe就可以理解成一个进程,而你用微信和别人视频,拿来传输文件,发送信息等等就有很多子任务,其中每一个任务就可以理解成线程。

其中多线程就是指,一个进程里面有多个线程,其优点在于解决了多部分同时运行的问题,提高效率;

而缺点是1.当线程太多会导致效率的降低,因为线程的执行依靠的是CPU的来回切换,而这个切换时非常损耗性能,过于频繁反而无法发挥出多线程编程的优势(通常减少上下文切换可以采用无锁并发编程,CAS算法,使用最少的线程和使用协程)。

    2.临界区线程安全问题,解决方法:(https://www.jianshu.com/p/959cf355b574)

2.同步和异步(Asynchronized和Synchronised)

多线程同步:就是在发出一个功能请求时,在没有得到结果之前,就不能发送下一个请求。好比所有跑道只剩一条的时候,运动员在一条跑道上跑完一个到一个

好处:解决了线程的安全问题。(线程的安全问题和非线程安全问题有区别吗??还没解决)

弊端:每次都有判断锁,浪费时间降低了效率。但是在安全与效率之间,首先考虑的是安全。

多线程异步:则是在发出一个功能请求时,不需要等待返回,随时可以再发送下一个请求。好比训练的时候,运动员在各自的跑道上跑自己的步。

好处:并发好,cpu利用率高。

弊端:要考虑线程间同步互斥问题等。

3.并发和并行

并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。

实际上,如果系统内只有一个CPU,而使用多线程时,那么真实系统环境下不能并行,只能通过切换时间片的方式交替进行,而成为并发执行任务。真正的并行也只能出现在拥有多个CPU的系统中。

4.阻塞和非阻塞

阻塞和非阻塞通常用来形容多线程间的相互影响,比如一个线程占有了临界区资源,那么其他线程需要这个资源就必须进行等待该资源的释放,会导致等待的线程挂起,这种情况就是阻塞,而非阻塞就恰好相反,它强调没有一个线程可以阻塞其他线程,所有的线程都会尝试地往前运行。

5.临界区

又可以称之为互斥区,用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每个线程使用时,一旦临界区资源被一个线程占有,那么其他线程必须等待。

如synchronize可以在任意对象及方法上加锁,而加锁的这段代码就被称为临界区。

二、如何创建一个线程

在Java中实现多线程有两种方法,1.继承Thread类,2.实现Runnable接口。(至于还有别的线程池或者是callable接口是这个基础上的增强版??之后会说到)

1.Thread类和Runnable接口的结构

public class Thread extends Object implements Runnable

Thread类实现Runnable接口,它们具有多态关系

可以传递Runnable接口的对象,而Runnable的设计是为了解决Java中单继承的限制。

还可以看出可以传递Thread类的对象,说明可以将一个Thread对象中的run()方法交给别的线程调用。

Runnable中只有一个run()方法

2.继承Thread类

1)创建一个新类继承Thread类。

2)重写Thread类中的public void run()方法,将线程的代码封装到run()里。

3)  创建该新类对象,创建线程。

4)对象调用start(),启动线程(即调用run方法)。

class MyThread extends Thread {  // 重载run函数  public void run() {  System.out.println("MyThread");}  public static void main(String argv[]) {  MyThread td = new MyThread(); // 创建,并初始化MyThread类型对象td  td.start(); // 调用start()方法执行一个新的线程  System.out.println("运行结束") }
} 

输出:运行结束MyThread因为“运行结束”是主线程main里的,所以是先执行

3.实现Runnable接口

1)定义一个新类,实现Runnable接口。

2)重写接口中run()方法,将线程的代码封装到run()里。

3)创建Runnable接口的子类对象。

4)将Runnabl接口的子类对象作为参数传递给Thread类的构造函数,创建Thread类对象(原因:线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务)。

  反正就是Runnable没有start()方法,需要通过传对象参数形式用线程类去启用它的run方法。

5)Thread类对象调用start(),启动线程(即调用run方法)。

class MyRunnable implements Runnable {  // 重载run函数  public void run() {  System.out.println("运行中!");}  public static void main(String argv[]) {  MyRunnable rb = new MyRunnable(); // 创建,并初始化MyRunnable对象rb  Thread td = new Thread(rb); // 通过Thread创建线程  td.start(); // 启动线程td  System.out.pritnln("运行结束");}
}  

输出:运行结束   运行中!

注意:调用线程的 run()方法是通过启动线程的start()方法来实现的。 因为线程在调用start()方法之后,系统会自动调用 run()方法。与一般方法调用不同的地方在于一般方法调用另外一个方法后,必须等被调用的方法执行完毕才能返回,而线程的 start()方法被调用之后,系统会得知线程准备完毕并且可以执行run()方法,start()方法就返回了,start()方法不会等待run()方法执行完毕。

为什么要重写run()方法呢?

因为不是类中的所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。

4.两种区别

两种创建线程性质都是一样的,但是通常建议创建线程是实现Runnable接口

优势是:1.可以支持多继承

     2.适合多个相同的程序代码的线程去处理同一个资源

    3.线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

三、线程的各个状态

理解下面的图!

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

四、Thread类的常用API方法(这里只是讲解方法基本,具体方法间比较后面会说到)

1.currentThread()(静态)

public static Thread currentThread()

返回对当前正在执行的线程对象的引用。注意是静态的

通常用作,Thread.currentThread.getName()返回线程名。

2.isAlive()

public final boolean isAlive()

测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。

返回:如果该线程处于活动状态,则返回 true;否则返回 false

3.sleep()(静态)

public static void sleep(long millis) throws InterruptedExceptionpublic static void sleep(long millis, int nanos) throws InterruptedException

在指定的毫秒数(或者加指定的纳秒数)内让当前正在执行的线程休眠(暂停执行)。注意是个静态方法

参数含义:millis - 以毫秒为单位的休眠时间。nanos - 要休眠的另外 0-999999 纳秒。

为什么要用sleep,主要是为了暂停当前线程,把cpu片段让出给其他线程,减缓当前线程的执行。

(后面再说和wait()的区别)

3.getId()

public long getId()

返回该线程的唯一标识。线程 ID 是一个正的 long 数,在创建该线程时生成。线程 ID 是唯一的,并终生不变。线程终止时,该线程 ID 可以被重新使用。

4.interrupt(),interrupted()(静态),isInterrupted()

public void interrupt()
public static boolean interrupted(){return currentThread().isInterrupted(true);
}
public boolean isInterrupted(){return isInterrupted( false);//至于为什么是false请看下面链接
}

interrupt():对线程进行中断操作。

isInterrupted():测试线程Thread对象是否已经是中断状态,但不清除状态标志,如果该线程已经中断,则返回 true;否则返回 false

interrupted():对当前线程进行中断操作(由上面源代码可知,就算当前是main线程用别的对象线程调用该方法,也还是判断返回main是否被中断),该方法会清除中断标志位为false。需要注意的是,当抛出InterruptedException时候,会清除中断标志位,也就是说此时再调用isInterrupted会返回false。

详细讲解这三个方法:https://www.cnblogs.com/w-wfy/p/6414801.html

5.yield()(静态)

public static void yield()

放弃当前的CPU资源,让它让给当前线程相同优先级的线程去占用CPU,即CPU暂停当前正在执行的线程对象,并执行其他线程。

但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

6.setDaemon() 守护线程Daemon

public final void setDaemon(boolean on)

将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

class ThreadDemo extends Thread {@Overridepublic void run() {for (int x = 0; x < 100; x++) {System.out.println(getName() + ":" + x);}}
}
public class test {public static void main(String[] args) {ThreadDemo td1 = new ThreadDemo();ThreadDemo td2 = new ThreadDemo();td1.setName("A");td2.setName("B");// 设置守护线程td1.setDaemon(true);td2.setDaemon(true);td1.start();td2.start();Thread.currentThread().setName("C");for (int x = 0; x < 5; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}}
}

当C执行完第五次之后(即C:4时候),A和B也会执行完,并不会执行100次

7.join()

public final void join() throws InterruptedException

等待该线程终止

class ThreadDemo extends Thread {@Overridepublic void run() {for (int x = 0; x < 100; x++) {System.out.println(getName() + ":" + x);}}}
public class test {public static void main(String[] args) {ThreadDemo ty1 = new ThreadDemo();ThreadDemo ty2 = new ThreadDemo();ThreadDemo ty3 = new ThreadDemo();ty1.setName("A");ty2.setName("B");ty3.setName("C");ty1.start();try {ty1.join();} catch (Exception e) {e.printStackTrace();}ty2.start();ty3.start();}
}

运行程序可以发现,名字A的线程运行完之后才开始运行B和C

五、Thread类和Object类一些相似的方法区别

sleep()和wait():

Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。

两者的区别在于:

1.sleep()方法是Thread的静态方法,而wait是Object实例方法。

2.wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁;而sleep()方法没有这个限制可以在任何地方种使用。

3.wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源;而sleep()方法只是会让出CPU并不会释放掉对象锁;

例子:https://www.cnblogs.com/DreamSea/archive/2012/01/16/2263844.html

4.sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行;而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。

sleep()和yield():

sleep()方法和yield()方法都是Thread类的静态方法(注意两个方法都是作用于当前线程),都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。

两者的区别在于:

1.sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。

2.当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态。

3.sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。

4.sleep()方法比yield()方法具有更好的可移植性,不能依靠yield()方法来提高程序的并发性能。对于大多数程序员来说,yield()方法的唯一用途是在测试期间人为地提高程序的并发性能,以帮助发现一些隐藏的错误。

runnble和callable

转载于:https://www.cnblogs.com/furaywww/p/8859140.html

Java 线程详解(一)线程的基础相关推荐

  1. Java多线程详解(线程不安全案例)

    嗨喽-小伙伴们我又来了, 通过前面两章的学习,我们了解了线程的基本概念和创建线程的四种方式. 附上链接: 1.  Java多线程详解(基本概念)​​​​​​​ 2. Java多线程详解(如何创建线程) ...

  2. java executors 详解_线程池—Executors 详解

    各位志同道合的朋友们大家好,我是一个一直在一线互联网踩坑十余年的编码爱好者,现在将我们的各种经验以及架构实战分享出来,如果大家喜欢,就关注我,一起将技术学深学透,我会每一篇分享结束都会预告下一专题 线 ...

  3. Java线程详解(11)-线程池

    Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序 ...

  4. Java线程详解(8)-线程的同步

    Java线程:线程的同步-同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段. 线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题, ...

  5. Java线程详解(6)-线程的交互

    线程交互是比较复杂的问题,SCJP要求不很基础:给定一个场景,编写代码来恰当使用等待.通知和通知所有线程. 一.线程交互的基础知识 SCJP所要求的线程交互知识点需要从java.lang.Object ...

  6. Java线程详解(4)-线程状态的转换

    一.线程状态 线程的状态转换是线程控制的基础.线程状态总的可以分为五大状态.用一个图来描述如下: 1.新状态:线程对象已经创建,还没有在其上调用start()方法. 2.可运行状态:当线程有资格运行, ...

  7. java线程详解_Java线程详解

    程序.进程.线程的概念程序(program):是为完成特定任务.用某种语言编写的一组指令的集合.即指一段静态的代码,静态对象. 进程(process):是程序的一次执行过程,或是正在运行的一个程序.动 ...

  8. Java线程详解(7)-线程的调度

    Java线程:线程的调度-休眠 Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率. 这里要明确的一点,不管程序员怎么编写调度,只能最大限度的影响线程执 ...

  9. Java线程详解(5)-线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...

  10. Java多线程详解(线程池)

    嗨喽-小伙伴们我来了, 上一章我们介绍了Java中的Thread类里一些常用的方法.本节我们就来聊一聊线程池. 说到"池",大家或许都不陌生,在java中,我们有见过数据库连接池, ...

最新文章

  1. 四种排序(冒泡、插入、递归、选择)
  2. Fiddler 自定义规则编写
  3. spg app android,GitHub - spgwzp/AndEsptouch: esptouch for android ,ESP8266网关配对
  4. SRM6.1安装配置指南
  5. 更改Android Studio中AVD的默认路径
  6. 程序员从幼稚到成熟会经历哪些变化?你都知道吗?
  7. 【赠书】拨云见日 - 深入解析Oracle TX行锁(下)
  8. 计算机用公式找出第一名,用公式查找Excel工作表中重复数据
  9. 软件2.0时代来了!特斯拉AI负责人说:神经网络正在改变编程
  10. PAT 1071. 小赌怡情(15)-PAT乙级真题
  11. HTML固定的底栏(flex布局)
  12. 影视后期好学吗?C4D精品教学合集,看完必成大神!(附链接)
  13. 基于C51的步进电机控制器设计
  14. 骁龙888打开“新象限” ,专业相机和游戏机直呼不讲武德
  15. 浅析精益生产中改善活动的8个步骤
  16. android双卡切换,OPPOReno双卡双待怎么切换使用?
  17. web前端入坑系列回归,再推荐一波学习资源
  18. html中的颜色代码详解及图示
  19. SpringBoot整合grpc
  20. 在c语言中 if语句后的一对原括号,c语言中if语句后的一对圆括号中

热门文章

  1. 小白学习MVC5+EF6遇到的问题一
  2. 在Eclipse/MyEclipse中安装spket插件
  3. resin3的优化配置
  4. 在你的 Linux 桌面嵌入终端窗口
  5. 基于Spring Security的认证方式_Spring Security 的认证流程_Spring Security OAuth2.0认证授权---springcloud工作笔记123
  6. 基于Session的认证方式_认证流程_Spring Security OAuth2.0认证授权---springcloud工作笔记114
  7. 大数据_Hbase-(概念补充_hbase中namespace的概念)---Hbase工作笔记0007
  8. 大数据_Hbase-原理说明_大数据存储_垂直拆表_水平拆表_动态列扩展---Hbase工作笔记0003
  9. mybatis工作笔记002_mybatis中如果返回的结果没有的话默认返回null的list_但可启用returnInstanceForEmptyRow_返回为list不为null但为0条
  10. 杭电1978 How many ways