一 线程和进程概念及其区别
进程:OS中一个程序的执行周期
线程:进程中的一个任务,一个进程可以包含n个线程
进程:操作系统中一个程序的执行周期为一个进程
进程线程区别
1.每个进程拥有自己的一整套变量,是操作系统中资源分配的最小单位。
线程依托于进程存在,多个线程共享进程资源,OS中任务调度的基本单位
2.启动,撤销一个进程的开销要比启动,撤销一个线程大的多(轻量级进程)
3.没有进程就没有线程,进程一旦终止,其内的线程全部撤销
进程状态
二 Java多线程实现
2.1继承Thread类实现多线程
java.lang.Thread是线程操作的核心类,JDK1.0提供。
新建一个线程最简单的方法就是直接继承Thread类而后覆写类中的run()方法(相当于主方法)
这里如果调用run()方法就会发现只是简简单单的顺序打印,并没有实现多线程
package www.Dyson.java;
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title=title;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(this.title+",i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) {
MyThread myThread1=new MyThread("thread1");
MyThread myThread2=new MyThread("thread2");
MyThread myThread3=new MyThread("thread3");
myThread1.run();
myThread2.run();
myThread3.run();
}
}
/*
thread1,i = 0
thread1,i = 1
thread1,i = 2
thread1,i = 3
thread1,i = 4
thread1,i = 5
thread1,i = 6
thread1,i = 7
thread1,i = 8
thread1,i = 9
thread2,i = 0
thread2,i = 1
thread2,i = 2
thread2,i = 3
thread2,i = 4
thread2,i = 5
thread2,i = 6
thread2,i = 7
thread2,i = 8
thread2,i = 9
thread3,i = 0
thread3,i = 1
thread3,i = 2
thread3,i = 3
thread3,i = 4
thread3,i = 5
thread3,i = 6
thread3,i = 7
thread3,i = 8
thread3,i = 9
*/
无论那种方式实现多线程,线程启动一律调用Thread类提供的start()方法
正确启动多线程
public class Test2 {
public static void main(String[] args) {
MyThread myThread1=new MyThread("thread1");
MyThread myThread2=new MyThread("thread2");
MyThread myThread3=new MyThread("thread3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
start()方法解析:
1.首先检查线程状态是否为0(线程默认状态为0表示未启动),如果线程已经启动再次调用start方法会抛出 IllegalThreadStateException(非受查异常)。
一个线程start()只能调用一次。
2.private native void start0() 通过start0真正将线程启动;
3.JVM调用start0方法进行资源分配与系统调度,准备好资源启动线程回调run()来执行线程具体任务!
native关键字指的是调用本机的原生系统函数
Thread类中有一个registerNatives本地方法,其定义在Thread.c文件中,它定义了各个操作系统平台都要用到的关于线程的公共数据和操作
java线程创建的调用流程图如下:
2.2 Runnable接口实现多线程 JKD1.0
解决单继承局限
1.Java中多线程的处理就是一个典型的代理模式。其中Thread类完成资源调度,系统分配辅助线程业务类;自定义的线程类负责真实业务实现。
2.使用Runable接口实现的多线程程序类可以更好的描述资源共享。
Thread类提供的构造方法:
public Thread(Runnable target)
Thread类可以接收Runnable接口对象
package www.Dyson.java;
class MyThread implements Runnable{
private String title;
public MyThread(String title){
this.title=title;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(this.title+",i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) {
MyThread myThread1=new MyThread("thread1");
MyThread myThread2=new MyThread("thread2");
MyThread myThread3=new MyThread("thread3");
new Thread(myThread1).start();
new Thread(myThread2).start();
new Thread(myThread3).start();
}
}
Runnable接口对象可以采用匿名内部类或者Lambda表达式来定义
范例:使用匿名内部类进行Runnable对象创建
public class Test2 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
}).start();
}
}
范例:使用Lambda表达式进行Runnable队形创建
public class Test2 {
public static void main(String[] args) {
Runnable runnable=()-> System.out.println("Hello world");
new Thread(runnable).start();
}
}
2.3 Callable<V>接口实现多线程 -- 唯一一个有返回值的线程
程实现方式 JDK1.5
juc:高并发程序包 <考的特别多,很重要>
java.util.concurrent.Callable
V call() throws Exception:线程执行完有返回值V
java.util.Future<V>:
V get() throws InterruptedException, ExecutionException:取得Callable接口call方法的返回值。
当线程需要返回值时只能采用Callable接口实现多线程。
范例:
class MyThread implements Callable<String> {
private int ticket=10;
@Override
public String call() throws Exception {
while(this.ticket>0){
System.out.println("剩余票数:"+this.ticket--);
}
return "票买完了,喵!";
}
}
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> tast=new FutureTask<>(new MyThread());
new Thread(tast).start();
new Thread(tast).start();
//调用get() 方法取得返回值
System.out.println(tast.get());
}
}
2.4 Thread与Runnable的区别
Runnable避免了单继承局限
Thread类是Runnable接口的子类
Runnable实现的多线程的程序类可以更好的描述程序共享的概念
三 多线程常用操作方法
3.1 线程的命名与取得
3.1.1 通过构造方法将线程命名
public Thread (String name)
public Thread(Runnable target,String name)
3.1.2 设置线程名称
public final synchronized void setName (String name)
3.1.3 取得线程名称
public final String getName()
3.1.4 取得当前正在执行的线程对象返回代码段正在被哪个线程调用的信息 ***** 重要 *****
public static native Thread currentThread()
以上方法使用范例:
class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("当前线程:"+Thread.currentThread().getName()+",i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread mt=new MyThread();
Thread thread1=new Thread(mt);
//没有设置名字
new Thread(mt).start();
//通过构造方法设置名字
new Thread(mt,"小❤❤").start();
//通过普通方法设置名字
thread1.setName("Liyuxin");
Java中的mian实际上是一个主线程(main) 主方法为主线程,依托于JVM进程
3.2 线程休眠(sleep)-- 单位为毫秒 运行态--阻塞态
线程休眠:指的是让线程暂缓执行,等到了预计时间在恢复执行。
线程休眠会立即交出CPU,让CPU去执行其他任务。线程休眠不会释放对象锁。
public static native void sleep(long millis)throws
InterruptedException(受查异常)
休眠时间是以毫秒作为单位的
范例:
class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("当前线程:"+Thread.currentThread().getName()+",i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread mt=new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
3.3 线程让步(yield) 运行态--就绪态
暂停当前正在执行的线程对象,并执行其他线程。
yield() 会让当前线程交出CPU,但不一定立即交出。yield()交出CPU后只能让拥有相同优先级的线程有获取CPU的机会。yield()不会释放对象锁。
public static native void yield()
范例:
class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
Thread.yield();
System.out.println("当前线程:"+Thread.currentThread().getName()+",i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread mt=new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
3.4 join()方法 本地方法
等待该线程终止。如果在主线程中调用该方法会让主线程休眠,让调用该方法的线程先执行完毕后再恢复执行主线程
public final void join() throws InterruptedException
join()只是对Object类wait()做了一层包装而已
范例:
class MyThread implements Runnable {
@Override
public void run() {
try {
System.out.println("主线程睡眠前的时间");
Test2.printTime();
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName());
System.out.println("睡眠时间结束");
Test2.printTime();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyThread mt=new MyThread();
Thread thread=new Thread(mt,"子线程A");
thread.start();
System.out.println(Thread.currentThread().getName());
thread.join();
System.out.println("代码结束");
}
public static void printTime(){
Date date=new Date();
DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time=format.format(date);
System.out.println(time);
}
}
当没有调用join方法时执行结果如下:
main
主线程睡眠前的时间
代码结束
2018-11-12 16:25:46
-------------------------
~~~ 睡眠五秒钟 ~~~
-------------------------
子线程A
睡眠时间结束
2018-11-12 16:25:51
当调用join方法后执行结果如下:
main
主线程睡眠前的时间
2018-11-12 16:29:58
-------------------------
~~~ 睡眠五秒钟 ~~~
-------------------------
子线程A
睡眠时间结束
2018-11-12 16:30:03
代码结束
3.5 线程停止
3.5.1 手工设置标志位,让线程在满足条件后退出 (推荐)
范例:
class MyThread implements Runnable {
private boolean flag=true;
@Override
public void run() {
int i=1;
while(flag){
try{
Thread.sleep(1000);
System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
i++;
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public void setFlag(boolean flag){
this.flag=flag;
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyThread mt=new MyThread();
Thread thread=new Thread(mt,"子线程A");
thread.start();
Thread.sleep(2000);
mt.setFlag(false);
System.out.println("代码结束");
}
}
3.5.2 使用stop方法强制让线程退出,但是该方法不安全已经被@Deprecated(过期声明)
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyThread mt=new MyThread();
Thread thread=new Thread(mt,"子线程A");
thread.start();
Thread.sleep(2000);
thread.stop();
System.out.println("代码结束");
}
}
3.5.3 使用Thread类提供的interrupt()中断线程(就是系统设置了一个标记位,直接使用即可)可以利用这个标志位自己手工设置
interrupt只是将线程状态改为中断状态而已,它不会中断一个正在运行的线程
如果线程调用了wait(),sleep(),join()进入阻塞态,调用该线程的interrupt()会抛出InterruptedException(受查异常,强制处理),在catch块里边处理该异常并break,并且将线程interrupt重置为false。
范例:
class MyThread implements Runnable {
private boolean flag=true;
@Override
public void run() {
int i=1;
while(flag){
try{
/*
这里阻塞之后,线程被调用了interrupte()方法,
清除中断标志,就会抛出一个异常
java.lang,InterruptedException
*/
Thread.sleep(1000);
boolean bool=Thread.currentThread().isInterrupted();
if(bool){
System.out.println("非阻塞情况下执行该操作... 线程状态"+bool);
break;
}
System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
i++;
}catch (InterruptedException e){
System.out.println("退出了");
/*
这里退出阻塞状态,且中断标志被系统会自动清除
并且重新设置为false,所以此处bool为fals
*/
boolean bool=Thread.currentThread().isInterrupted();
System.out.println(bool);
return;
}
}
}
public void setFlag(boolean flag){
this.flag=flag;
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyThread mt=new MyThread();
Thread thread=new Thread(mt,"子线程A");
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("代码结束");
}
}
3.6 线程优先级
线程的优先级是指,优先级越高越有可能先执行而已,仅仅是有可能而已
设置优先级
public final void setPriority(int newPriority)
取得优先级
public final int getPriority()
Thread.Max_PRIORITY = 10; 高
Thread.NORM_PRIORITY = 5;中
Thread.MIN_PRIORITY = 1; 低
范例:设置优先级
class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("当前进程:"+Thread.currentThread().getName()+", i = "+i);
}
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
Thread thread1 = new Thread(mt, "1");
Thread thread2 = new Thread(mt, "2");
Thread thread3 = new Thread(mt, "3");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.NORM_PRIORITY);
thread3.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
thread3.start();
/*
执行顺序:3号线程 -> 2号线程 -> 1号线程
*/
}
}
线程是有继承性的:优先级可以继承
在A线程中启动B线程,则A和B的优先级一样
主方法只是一个中等优先级
范例:观察线程的继承性
class A implements Runnable {
@Override
public void run() {
System.out.println("A的优先级为:"+Thread.currentThread().getPriority());
//在A线程中启动B线程
Thread thread=new Thread(new B());
thread.start();
}
}
class B implements Runnable{
@Override
public void run() {
System.out.println("B的优先级为:"+Thread.currentThread().getPriority());
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new A());
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
}
3.7 守护线程
守护线程是一种特殊的线程,属于陪伴线程。
Java中两种线程:用户线程,守护线程。isDameon(),返回true是守护线程,返回false是用户线程
典型的守护线程:垃圾回收
只要当前JVM进程中存在任何一个用户线程没有结束,守护线程就一直工作;只有当最后一个用户线程停止后,守护线程会随着JVM进程一同停止。
setDaemon() 将当前线程设置为守护线程,必须在start(),之前执行
java中启动线程默认为用户线程,包括main线程
范例:
class A implements Runnable{
private int i;
@Override
public void run() {
try{
while(true){
i++;
System.out.println("线程名称:"+Thread.currentThread().getName()+",i="+i+",是守护线程"+
Thread.currentThread().isDaemon());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("线程名称:"+Thread.currentThread().getName()+"中断线程了");
}
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(new A(),"子线程A");
//手工设置线程A为守护线程
thread1.setDaemon(true);
thread1.start();
Thread thread2=new Thread(new A(),"子线程B");
thread2.start();
Thread.sleep(3000);
//中断非守护线程
thread2.interrupt();
Thread.sleep(10000);
System.out.println("代码结束");
}
}