Java多线程

一.线程与进程

多线程中的名词解释:

1.线程:进程中负责程序执行的执行单元,线程本身依靠程序进行运行,线程是程序中的顺序控制流,只能使用分配给程序的资源和环境。

2.进程:执行中的程序一个进程至少包含一个线程。

3.单线程:程序中只存在一个线程,实际上主方法就是一个主线程。

4.多线程:在一个程序中运行多个任务目的是更好地使用CPU资源。

5.并行:多个CPU实例或者多台机器同时执行一段处理逻辑,是真正的同时。

6.并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时,并发往往在场景中有公用的资源。

7.线程安全:并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。

8.同步:Java中的同步指的通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。

二.线程的实现

1.继承Thread类

在java.lang包中定义,继承Thread类必须重写run()方法;

线程类:

public class MyThread extends Thread{
public static int num =0;
public MyThread(){
	num++;
}
/**
 * run()方法用来执行线程定义的任务
 */
@Override
public void run (){
	System.out.println("主动创建第"+num);
}
}

线程调用:

	public class Test{
		public static void main(String[] args) {
			MyThread thread =new MyThread();
			//通过start()方法去调用线程
			thread.start();
		}
	}

创建好了自己的线程类之后,就可以创建线程对象,然后通过start()方法区启动线程。不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务,等于在main线程中调用run()方法。

区分start()和run()方法:

public class MyThread extends Thread{
private String name;
public MyThread(String name){
	this.name=name;
}
/**
 * run()方法用来执行线程定义的任务
 */
@Override
public void run (){
	//查看线程ID的方法
	System.out.println("子线程ID:"+name+"-"+Thread.currentThread().getId());
}
}
	public class Test{
		public static void main(String[] args) {
			System.out.println("主线程的ID:"+Thread.currentThread().getId());
			MyThread thread1 =new MyThread("Thread1");
			thread1.start();
			MyThread thread2 =new MyThread("Thread2");
			thread2.run();
		}
	}

结论:

(1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别。

(2)虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。 

2.实现Runable接口

public class MyThread implements Runnable{
private String name;
public MyThread(String name){
	this.name=name;
}
/**
 * run()方法用来执行线程定义的任务
 */
@Override
public void run (){
	//查看线程ID的方法
	System.out.println("子线程ID:"+name+"-"+Thread.currentThread().getId());
}
}
	public class Test{
		public static void main(String[] args) {
			System.out.println("主线程的ID:"+Thread.currentThread().getId());
			MyThread thread1 =new MyThread("Thread1");
			Thread thread =new Thread(thread1);
			thread.start();
			MyThread thread2 =new MyThread("Thread2");
			Thread thread3 =new Thread(thread2);
			thread3.run();
		}
	}

三.使用ExecutorService、Callable、Future实现有返回结果的多线程

有返回值的任务必须实现Callable接口,无返回值的任务必须Runable接口。执行Callable任务后,可以获取一个Future对象,在该对象上调用get就可以获取Callable任务返回的Object,再结合线程池接口ExecutorService就可以实现由返回结果的多线程。

import java.util.Date;
import java.util.concurrent.Callable;

public class MyCallable implements Callable<Object>{
private String taskNum;
public MyCallable(String taskNum){
	this.taskNum=taskNum;
}
@Override
public Object call() throws Exception {
	System.out.println(">>>"+taskNum+"任务启动");
	Date datetemple1 =new Date();
	Thread.sleep(2000);
	Date datetemple2 =new Date();
	long time =datetemple2.getTime()-datetemple1.getTime();
	System.out.println(">>>"+taskNum+"任务终止");
	return taskNum+"任务返回运行结果,当前任务时间"+time+"毫秒";
}

}
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
	public class Test{
		public static void main(String[] args) throws InterruptedException, ExecutionException {
           System.out.println("程序开始运行");
           Date date1 = new Date();
           int taskNum = 5;
           //创建一个数组为5的线程池
           ExecutorService pool = Executors.newFixedThreadPool(taskNum);
           //创建多个有返回值的任务
           List<Future>list =new ArrayList<Future>();
           for(int i=0;i<taskNum;i++){
        	   Callable c =new MyCallable(i+"");
        	   Future f =pool.submit(c);
        	   list.add(f);
           }
           pool.shutdown();
           for(Future f:list){
        	   System.out.println(">>>"+f.get().toString());
           }
           Date date2 = new Date();
           System.out.println("---程序结束运行---"+(date2.getTime()-date1.getTime()));
		}
	}

四.线程的状态

1.创建(new)状态:准备好一个多线程的对象

2.就绪(runable)状态:调用start()方法,等待CPU进行调度

3.运行(running)状态:执行run()方法

4.阻塞(blocked)状态:暂时停止执行,可能将资源交给其它线程使用

5.终止(dead)状态:线程销毁

(1)New一个线程对象start()方法——通过JVM分配内存到达Runable状态——通过获取CPU资源到达Running状态——执行run方法——线程销毁(单线程执行步骤)

(2)yield()方法:当前运行线程释放处理器资源,从running状态回到Runable状态。

(3)join()方法:主要作用于同步,使得线程之间的并行执行变为串行执行。join的意思是放弃当前线程的执行,并返回对应的线程。在A线程中调用了B线程的join()方法,表示只有当B线程执行完毕时,A线程才能继续执行。

(4)sleep()方法:

sleep和wait的区别:

*sleep是Thread类的方法,wait是Object类中定义的方法

*Thread.sleep不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁

*Thread.sleep和Object.wait都会暂停当前的线程,OS会将执行时间分配给其他线程,区别是,调用wait后,需要别的线程执行notify/notifyAll才能重新获取CPU执行时间

上下文切换:

对于单核CPU来说,CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另一个线程,这个叫做线程上下文切换。


静态方法:currentThread()方法可以返回代码段正在被哪个线程调用的信息。

sleep()方法

方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠。

sleep相当于让线程睡眠,让CPU去执行其他任务。

yield()方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,yield方法只能让拥有相同优先级的线程获取CPU执行时间的机会。

public class MyThread extends Thread{
/**
 * run()方法用来执行线程定义的任务
 */
@Override
public void run(){
    long beginTime=System.currentTimeMillis();
    System.out.println(beginTime);
    int count=0;
    for (int i=0;i<50000000;i++){
        count=count+(i+1);
        Thread.yield();
    }
    long endTime=System.currentTimeMillis();
    System.out.println(endTime);
    System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
}
}

getId():getId()的作用是取得线程的唯一标识

join()方法,主线程创建并启动了线程,如果子线程中要进行大量耗时运算,主线程早于子线程之前结束。比如子线程处理一个数,主线程要用子线程的数据,通过join()方法,让主线程给子线程让路。join()的作用是等待线程对象销毁。

public class MyThread extends Thread{
	public MyThread(String name){
		super(name);
	}
/**
 * run()方法用来执行线程定义的任务
 */
@Override
public void run(){
for(int i=0;i<5;i++){
	System.out.println(getName()+" "+i);
}
}

public class Test1 {
public static void main(String[] args) throws InterruptedException {
new MyThread("new Thread").start();
for(int i=0;i<10;i++){
	if(i==5){
		MyThread th =new MyThread("Join Thread");
		th.start();
		th.join();
	}
	System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}

getName和setName 用来得到或者设置线程名称。

getPriority和setPriority 用来获取和设置线程优先级

setDaemon和isDaemon 用来设置线程是否成为守护线程和判断线程是否是守护线程

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也随着消亡。守护线程依赖于main线程,main运行完,守护线程也结束。



停止线程

1.使用退出标志,使线程正常退出,当run方法完成后线程终止

2.使用stop方法强行终止线程,不推荐使用

3.使用interrupt方法中断线程,但不会终止一个正在运行的线程,还需要假如一个判断才可以完成线程的停止。

interrupt()方法

线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也是CPU优先执行优先级较高的线程对象中的任务。

线程优先级特性:

1.继承性

A线程启动B线程,则B线程的优先级与A是一样的

2.规则性

高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。

3.随机性

优先级高的线程不一定每一次都先执行完





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值