1、 Java中线程与进程
1.1、什么是进程?
进程就是一个应用程序,APP,操作系统。
线程是进程中一个最小的独立执行任务的单元,一个进程至少含有一条线程。
进程占用操作系统的资源,线程占用进程的资源。
(在我看来,进程就相当于公司市场部接了个活,线程就是把这个活布置给公司各个员工去办)
1.2、进程与线程的区别?
1、进程,可以理解为一个应用程序。线程在进程中用来执行任务的一个最小的执行单元。
2、进程占用系统的资源,线程占用进程中的资源。
3、进程是由操作系统来调度。线程由CPU调度。
4、线程的执行由CPU是否分配时间给线程来决定线程的运行。
2、如何创建线程
方法一:外部类继承Thread类,重写run()方法;
方法二:写一个外部类实现Runnable接口。
方法三:写一个内部类实现Runnable接口。
代码如下:
(方法一和二)
package day07;
/*
* JAVA中提供了Thread线程类,这个类就是定义了一个run方法,
* run方法就是定义线程执行的任务(线程要干的活)
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//创建线程
MyThread1 t1=new MyThread1();
//启动线程
t1.start();
//创建线程任务
MyRunnable r=new MyRunnable();
//创建线程
Thread t2=new Thread(r);
t2.start();
}
}
//外部类继承Thread类,重写run()方法
class MyThread1 extends Thread{
//他这里面有一个run方法,用来定义线程要执行的任务
public void run() {
for(int i=0;i<100;i++) {
System.out.println("今天天气真好");
}
}
}
//写一个类实现Runnable接口
class MyRunnable implements Runnable{
//实现接口,定义线程要执行的任务
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++) {
System.out.println("ok");
}
}
}
测试:
(方法三)
package day07;
/*
* JAVA中提供了Thread线程类,这个类就是定义了一个run方法,
* run方法就是定义线程执行的任务(线程要干的活)
*/
public class ThreadDemo2 {
public static void main(String[] args) {
//使用匿名内部类的方式创建线程
Thread t1=new Thread(){
public void run() {
System.out.println("这里是匿名内部类创建线程");
}
};
//启动线程
/*
* 当线程调用start()方法,线程就会立马执行吗
* 当线程调用start()方法的时候,线程并不会立马执行
* 这时候线程处于就绪状态
* 当CPU给线程分配时间片段的时候,线程才会进入到执行的状态
*/
t1.start();
//使用匿名内部类创建Runnable接口的实现类
Runnable runnable=new Runnable() {
//不知道他的名字,所以要重写他的方法
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("使用匿名内部类创建Runnable接口的实现类");
}
};
Thread t2=new Thread(runnable);
//启动t2线程
t2.start();
}
}
测试:
3、线程的生命周期(线程的状态)
1、新建状态:创建线程时Thread t=new Thread();
2、就绪状态:线程调用start()方法时。
3、运行状态:线程得到CPU分配的时间片以后线程进入运行状态。
4、阻塞状态:当线程调用sleep()方法时。
5、死亡状态:当run()方法执行完毕以后。
4、线程相关的API
4.1、currentThread方法(查看当前线程):
1、新建类TestCurrentThread,在该类中创建方法testCurrent实现输出当前线程。
2、在TestCurrentThread类的main方法中,输出当前线程并调用testCurrent查看当前线程。
3、在main方法中,使用内部类创建线程t,该线程中实现输出当前线程并调用testCurrent查看当前线程。
4、启动线程t。
4.2、long getId():返回该线程的标识符。
4.3、String getName():返回该线程的名称。
4.4、int getPriority():返回该线程的优先级。
4.5、Thread.state.getState():获取该线程的状态。
4.6、boolean isAlive():测试线程是否处于活动状态
4.7、boolean isDaemon():测试线程是否为守护线程
4.8、boolean isInterrupted():测试线程是否已经中断
代码:
package day07;
public class ThreadDemo04 {
public static void main(String[] args) {
//获取运行当前方法的线程
Thread tt=Thread.currentThread();
System.err.println("tt:"+tt);
System.out.println(tt.getName());
long id=tt.getId();//获取线程的唯一标识
System.out.println(id);
//isAlive判断线程是否存活
System.out.println(tt.isAlive());
//判断线程是否是后台线程
System.out.println(tt.isDaemon());
//判断线程是否被中断
System.out.println(tt.isInterrupted());
//创建线程
Thread t=new Thread();
System.out.println(t.getName());//线程名称
}
}
测试:
从上往下分别是:查看当前线程、第一条线程名称、线程优先级、线程是否存活、线程是否为后台进程、线程是否被中断、第二条线程的名称。
5、线程优先级的设置
在这里插入图片描述线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最大程度的改善线程获取时间片的几率。
线程的优先级被划分为10级,值分别1-10,其中1最低,10最高。线程提供了3个常亮来表示最低,最高,以及默认优先级。
Thread.MIN_PRIORITY,(最低)
Thread.MAX_PRIORITY,(最高)
Thread.NORM_PRIORITY,(默认)
方法:void setPriority(int priority):设置线程的优先级。
代码:
package day07;
/*
* 线程的优先级
* 线程一般可以设置优先级,一般的情况,优先级高的线程就有可能
* 优先得到CPU分配的时间片段
*
* 线程的优先级的级别从1到10,最高是10
*/
public class ThreadDemo05 {
public static void main(String[] args) {
//创建第一条线程
Thread max=new Thread() {
//重写
public void run() {
for(int i=0;i<1;i++) {
System.out.println("优先级最高");
}
}
};
//创建第二条线程
Thread nor=new Thread() {
//重写
public void run() {
for(int i=0;i<1;i++) {
System.out.println("默认优先级");
}
}
};
//创建第三条线程
Thread min=new Thread() {
//重写
public void run() {
for(int i=0;i<1;i++) {
System.out.println("优先级最低");
}
}
};
//给线程设置优先级
max.setPriority(10);
min.setPriority(1);
//启动线程
max.start();
nor.start();
min.start();
}
}
测试:(注意优先级只是提高概率,并不是百分百按照优先级顺序执行的)
6、守护线程
守护线程与普通线程在表现上没有什么区别,我们只需要通过Thread提供的方法来设定即可:
void setDaemon(boolean )
当参数为true时该线程为守护线程。
守护线程的特点是,当进程中只剩下守护线程时,所有守护线程强制终止。
GC(Garbage Collection 垃圾回收机制)就是运行在一个守护线程上的。
代码:
package day08;
/*
* 守护线程,当一个程序的所有普通线程都结束,
* 那么守护线程也会被强制终止,进程就会被退出
*
* 自己创建出来的线程就是普通线程,包括主线程也是普通县城
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Thread t1=new Thread() {
public void run() {
for(int i=0;i<10;i++) {
System.out.println("Rose:jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("跳完了");
}
};
Thread t2=new Thread() {
public void run() {
for(int i=0;i<10;i++) {
System.out.println("Jack:You jump I jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
/*
* 将t2设置为后台进程,但是要注意的是,必须要在线程启动之前来设定后台线程
* 目的让虚拟机知道这条线程是后台线程
*/
t2.setDaemon(true);
//启动线程
System.out.println("主线开始");
t1.start();
t2.start();
System.out.println("主线结束");
}
}
测试:(在Rose跳完后,Jack(守护线程)则不再说话(终止))
7、线程的sleep()方法
Thread的静态方法sleep用于使当前线程进入阻塞状态:
static void sleep(long ms)
该方法会使当前线程进入阻塞状态执行毫秒,当阻塞指定毫秒后,当前线程会重新进入Runnable状态,等待分配时间片。
该方法声明抛出一个InterruptException。所以在使用该方法时需要捕获这个异常。
代码:
package day07;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* Thread 的sleep()的静态方法
* 凡是线程调用了sleep()方法之后,当前的线程就会进入阻塞状态
*
*/
public class ThreadDemo06 {
public static void main(String[] args) {
//要求每隔一秒钟向控制台输出当前的系统时间
Thread t=new Thread() ;
//每隔一秒钟向控制台输出当前的系统时间
//让线程睡眠(要加循环改进)
try {
for(int i=0;i<5;i++) {
//获取当前系统时间
Date date=new Date();//注意导入util包
SimpleDateFormat sd=new SimpleDateFormat("HH:mm:ss");//格式化时间
//格式化
String time=sd.format(date);
System.out.println(time);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//启动
t.start();
};
}
测试:
8、线程join方法
Thread的方法join:
void join()
该方法用于等待当前线程结束。
该方法声明抛出InterruptException。
代码:
package day08;
/**
* 线程的join方法
* 当在一条中调用了另外一条线程,当前线程就会等待调用线程执行完
* 再执行自己的线程代码
* @author lenovo
*
*/
public class ThreadDemo02 {
private static boolean isOver=false;
public static void main(String[] args) {
//模拟下载
Thread t1=new Thread() {
public void run() {
System.out.println("开始下载...");
for(int i=0;i<=100;i++) {
System.out.println("已完成"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
isOver=true;
System.out.println("下载完毕");
}
};
//模拟图片显示
Thread t2=new Thread() {
public void run() {
System.out.println("开始下载图片");
//调用t1线程的join方法,等待t1先执行完毕
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!isOver) {
throw new RuntimeException("下载失败");
}
System.out.println("图片加载成功");
}
};
t1.start();
t2.start();
}
}
测试:(如果没有join方法,则会在没加载到100%时执行出下载失败命令)
9、线程的yield()方法
Thread的静态方法yield:
static void yield()
该方法用于使当前线程主动让出档次CPU时间片回到Runnable状态,等待分配时间片。
10、线程同步与异步
关于多用户同一时刻访问服务器的时候,有可能造成线程并发的问题。
多线程:服务器端有多条线程同时运行
多线并发问题:
优点:程序执行效率高
缺点:对于多线程同时访问同一资源的时候,有可能造成数据不安全
10.1、什么是线程同步?
是指线程的执行是有顺序,同步线程在访问同一资源的时候,可以保证数据的安全性,但是执行的效率较慢。
10.2、什么是线程异步?
是指线程的执行是各干各的,互不干扰,执行效率较高,但是,对于异步访问同一重要数据的时候,有可能出现线程安全问题。
11、线程的执行是随机的,主要看CPU分配。
比如这个代码:
package day07;
/*
* 获取线程相关信息
* 运行main方法的线程,是一个主线程,由虚拟机创建的线程
*/
//这个Demo说明线程是并行的,给我们的视觉效果就是多条线程同时运行
//但是实际上,每条线程的运行都是要得到CPU分配的时间才可以执行的
//没有得到CPU分配的时间的线程会处于一种就绪的状态
public class ThreadDemo03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("程序开始");
//匿名内部类
Thread t1=new Thread() {
public void run() {
for(int i=0;i<10;i++){
//验证线程启动后是并行运行
System.out.println("aaa");
}
}
};
//启动线程
t1.start();
//匿名内部类
Thread t2=new Thread() {
public void run() {
for(int i=0;i<10;i++){
//验证线程启动后是并行运行
System.out.println("??????");
}
}
};
t2.start();
System.out.println("程序结束");
}
}
多次测试结果:
表示CPU是将时间片随机分给各个线程的。