文章目录
- 一、线程和进程
- 二、线程的生命周期
- 三、创建线程的三种方式
- 1.继承Thread类
- 2.实现Runnable接口
- 3.实现Callable接口
- 四、创建线程对象的3种方法
- 五、线程控制阻塞的几种方式
- sleep()方法
- join()方法
- yield()方法
- 守护线程
一、线程和进程
什么是JUC? java.util.concurrent 也就是java工具类,在并发编程中用到
进程:进程是程序一个动态执行的过程。进程是操作系统资源分配的最小单位。进程之间相互独立。一个进程由一个或多个线程组成.
线程: 线程是进程执行的最小调度单位。多个线程共享一个进程下的资源,且一个进程下线程相互影响。一个进程内的线程在其他进程内是不可见;
Java默认有两个线程: 主(mian)线程、GC线程
进程的一些特性:
线程和进程的比较:
java能否能开启线程?
不能,线程是由操作系统实现的,而java是运行在虚拟机之上的,无法直接操作硬件。java开启线程是通过底层调用了本地的C++方法。
//Thread底层的几个主要方法
private static native void registerNatives();
public static native Thread currentThread();
public static native void yield();
public static native void sleep(long millis) throws InterruptedException;
public final native boolean isAlive();
Thread底层的相关方法都是被 native 关键字所修饰的。
在 Java 的 API 中,一个 native 方法往往意味着这个方法无法使用平台无关的手段来实现。
所以,线程的实现与 Java 无关,由平台所决定,Java 所做的是将 Thread 对象映射到操作系统所提供的线程上面去,对外提供统一的操作接口
这也是 Java 这门语言诞生之初的核心思想,一处编译,到处运行,只面向虚拟机,实现所谓的平台无关性,而这个平台无关性就是由虚拟机为我们提供的。
查看线程底层实现:
new Thread().start();
点开start()方法
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
首先。start()是一个synchronized方法,同步方法,安全,这个方法会把当前线程加入一个线程组,调用了start0()方法,这个start0(),是用native修饰的,也就是本地方法。所以最后是调用了本地方法,java是没有权限开启线程的。start()调用了本地的C++方法,因为java是运行在虚拟机之上的,无法直接操作硬件。
并发、并行
并发: cpu只有一个核:多线程操作同一个资源(cpu通过线程间的快速交替,模拟出来多条线程,看似并行,实际串行)
并行: 多个人一起行走,cpu有多个核,多个线程可以同时执行,可以通过线程池完成
并发编程的本质: 充分利用CPU的资源
二、线程的生命周期
Thread.State
点进State可以看到线程的生命周期
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
三、创建线程的三种方式
1.继承Thread类
创建线程的第一种方法。继承java.lang包的Thread类。
Thread类也是通过Runnable接口来创建线程对象。本质上Thread是Runnable的一个包装类。
package com.wdy.thread;
/*
* 创建线程的第一种方法。继承java.lang包的Thread类
*
*/
public class ThreadDemo01 extends Thread { //继承了Thread类。产生的对象是线程对象
private String name;//线程名称
public ThreadDemo01(String name) {
this.name=name;
}
@Override
public void run() { //覆盖run方法
for(int i=1;i<=10;i++) {
System.out.println(this.name+"-"+i);
try {
sleep(200); //线程阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1=new ThreadDemo01("A"); //创建3个线程对象
Thread t2=new ThreadDemo01("B");
Thread t3=new ThreadDemo01("C");
t1.start(); //线程对象就绪
t2.start();
t3.start();
}
}
执行结果:从结果可以看出多个线程是交错执行的。
2.实现Runnable接口
因为java是单继承,继承了Thread类,就不能继承其它类。所以第二种方法用Runnable接口来实现,在实现Runnable接口的同时还能继承一个类,在实际项目中运用较多。
package com.wdy.thread;
/*
* 创建线程的第二种方法。实现Runnable接口
* 和方法一不同的地方在于阻塞 和创建线程对象
*
*/
public class ThreadDemo02 implements Runnable { //继承了Thread类。产生的对象是线程对象
private String name;//线程名称
public ThreadDemo02(String name) {
this.name=name;
}
@Override
public void run() { //覆盖run方法
for(int i=1;i<=10;i++) {
System.out.println(this.name+"-"+i);
try {
Thread.sleep(200); //阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1=new Thread(new ThreadDemo02("A")); // 创建3个线程对象 Thread.Runnable()方法
Thread t2=new Thread(new ThreadDemo02("B")); // 创建3个线程对象
Thread t3=new Thread(new ThreadDemo02("C")); // 创建3个线程对象
t1.start(); //线程对象就绪
t2.start();
t3.start();
}
}
执行结果:
3.实现Callable接口
唯一一种有返回值的。
在第三种方法。通过Callable接口创建线程对象使用的也是Runnable接口的实现类FutureTask()。
package com.wdy.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo03 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int s=0;
for(int i=1;i<=10000;i++) {
s+=i;
}
return s;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> ft=new FutureTask<Integer>(new ThreadDemo03());
ft.run();
System.out.println("结果为:"+ft.get());
}
}
执行结果:
四、创建线程对象的3种方法
- 通过类来创建线程对象
- 通过匿名内部类
- 通过函数式编程
Runnable接口实现了FunctionalInterface接口(函数接口)
package com.wdy.thread;
public class ThreadDemo02 implements Runnable { //继承了Thread类。产生的对象是线程对象
private String name;//线程名称
public ThreadDemo02(String name) {
this.name=name;
}
@Override
public void run() { //覆盖run方法
for(int i=1;i<=10;i++) {
System.out.println(this.name+"-"+i);
try {
Thread.sleep(200); //阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*第一种 通过类创建线程*/
public static void main(String[] args) {
// Thread t1=new Thread(new ThreadDemo02("A")); // 创建3个线程对象 Thread.Runnable()方法
// Thread t2=new Thread(new ThreadDemo02("B")); // 创建3个线程对象
// Thread t3=new Thread(new ThreadDemo02("C")); // 创建3个线程对象
//
// t1.start(); //线程对象就绪
// t2.start();
// t3.start();
//
/*第二种 通过匿名内部类创建线程*/
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<=10;i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(200); //阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
// /*第三种 通过函数式接口创建线程*/
// new Thread(()->{
//
// for(int i=1;i<=10;i++) {
// System.out.println(Thread.currentThread().getName()+"-"+i);
// try {
// Thread.sleep(200); //阻塞
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
//
// }).start();;
}
}
五、线程控制阻塞的几种方式
sleep()方法
sleep()实现线程阻塞的方法,我们称之为“线程睡眠”,方式是超时等待,就是sleep()通过传入“睡眠时间”作为方法的参数,时间一到就从“睡眠”中“醒来”;
join()方法
在t1.start()之后,对t1线程实现join() ,阻塞其它线程,控制t1线程优先执行完毕。
package com.wdy.thread;
public class ThreadBlockDemo01 extends Thread{
private String name;//线程名称
public ThreadBlockDemo01(String name) {
this.name=name;
}
public void run() { //覆盖run方法
for(int i=1;i<=10;i++) {
System.out.println(this.name+":"+i);
try {
sleep(200); //阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new ThreadDemo02("A")); // 创建3个线程对象 Thread.Runnable()方法
Thread t2=new Thread(new ThreadDemo02("B")); // 创建3个线程对象
Thread t3=new Thread(new ThreadDemo02("C")); // 创建3个线程对象
t1.start(); //线程对象就绪
t1.join(); //阻塞线程A,让A线程优先执行
t2.start();
// t2.join();//阻塞线程B,让B线程优先执行
t3.start();
}
}
同理,在t2.start()之后,再加一个join() ,控制t2线程优先执行。执行结果如下:
yield()方法
package com.wdy.thread;
public class ThreadBlockDemo3 extends Thread{
private String name;//线程名称
public ThreadBlockDemo3(String name) {
this.name=name;
}
public void run() { //覆盖run方法
for(int i=1;i<=10;i++) {
if(i%2==0) {//当前线程为偶数,暂时让出cpu运行时间片
Thread.yield();
}
System.out.println(this.name+":"+i);
try {
sleep(200); //阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new ThreadDemo02("A")); // 创建3个线程对象 Thread.Runnable()方法
Thread t2=new Thread(new ThreadDemo02("B")); // 创建3个线程对象
Thread t3=new Thread(new ThreadDemo02("C")); // 创建3个线程对象
t1.start(); //线程对象就绪
t2.start();
t3.start();
}
}
守护线程
package com.wdy.thread;
public class Demo2 extends Thread {
@Override
public void run() {
while(true) {
System.out.println("线程运行中");
try {
sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t=new Demo2();//主线程
t.setDaemon(true);//守护线程:主线程结束,其它线程也结束
t.start();
for(int i=0;i<10;i++) {
System.out.println(i);
Thread.sleep(200);
}
}
}
不加守护线程,会陷入死循环。结果如下
加入守护线程的运行结果如下
t.setDaemon(true);主线程终止之后,其余线程也会被终止。