一、线程和进程
进程(Process):
1、是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
2、在早期面向进程设计的计算机结构中,进程是程序的基本执行实体。
3、在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程(thread):
1、看成是进程中的最小单元。
2、一个进程中可以有1个或者多个线程(其实也就是一小段代码)。
3、 线程其实也可以看成是一个轻量级的进程(比如1个进程只有1个线程)。
4、在单个程序中同时运行多个线程完成不同的工作,称为多线程。
CPU:执行任务是以线程为单位;会在不同的时间片上不断的切换线程。
二、启动线程两种方式
1、启动线程两种方式:
1、通过继承Thread 类
1 /** 2 * 继承thread类 3 */ 4 public class ThreadMusic extends Thread { 5 @Override 6 public void run(){ 7 synchronized (this){ //同步 8 for (int i =0; i< 100; i ++){ 9 System.out.println(this.getName() + "------听音乐------" + i); 10 } 11 } 12 } 13 }
2、实现 Runnable 接口
区别:一个类如果继承了其他类,就无法在继承Thread类,在Java中,一个类只能继承一个类,而一个类如果实现了一个接口,还可以实现其他接口,接口是可以多实现的,所以说Runable 的扩展性更强。
1 /** 2 * 实现runnable接口 3 */ 4 public class RunnableMusic implements Runnable { 5 @Override 6 public void run() { 7 synchronized (this){ //同步 8 for (int i =0; i< 100; i ++){ 9 System.out.println(Thread.currentThread().getName() + "------听音乐------" + i); 10 } 11 } 12 } 13 }
2、启动线程流程:
创建启动线程的方式一:继承Thread类
① 明确需要把什么事情封装成线程对象;
② 自定义类 extends Thread类
③ 覆写run方法: 写①
④ 创建自定义对象t
⑤ 启动线程 t.start();
创建启动线程方式二:实现Runnable接口
①明确需要把什么事情封装成线程对象;
② 自定义类 implements Runnable接口
③ 覆写run方法: 写①
④ 创建自定义对象t
⑤ 启动线程 new Thread(t).start();
测试线程:
1 public class testThread { 2 /** 3 * 测试threadMusic 4 */ 5 @Test 6 public void testThread(){ 7 ThreadMusic music = new ThreadMusic(); 8 music.start(); 9 } 10 11 /** 12 * 测试threadMusic 13 */ 14 @Test 15 public void testRunnable(){ 16 RunnableMusic runnableMusic = new RunnableMusic(); 17 Thread thread = new Thread(runnableMusic); 18 thread.start(); 19 } 20 }
3、线程方法
1、Thread类的方法
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
1 /** 2 * 测试sleep方法 3 */ 4 public class SleepTest{ 5 public static void main(String[] args) throws InterruptedException { 6 for(int i = 1; i<11; i++ ){ 7 System.out.println(i); 8 Thread.sleep(1000); //休眠1秒 9 } 10 } 11 }
2、获取Thread的名称
1 /** 2 * 继承thread类 3 */ 4 public class ThreadMusic extends Thread { 5 @Override 6 public void run(){ 7 synchronized (this){ 8 for (int i =0; i< 100; i ++){ 9 System.out.println(this.getName() + "------听音乐------" + i); //获取Thread的名称 10 } 11 } 12 } 13 }
3、获取Runnable的名称
1 /** 2 * 实现runnable接口 3 */ 4 public class RunnableMusic implements Runnable { 5 @Override 6 public void run() { 7 synchronized (this){ //同步 8 for (int i =0; i< 50; i ++){ 9 //获取Runnable的名称, currentThread()方法,返回当前正在执行的线程引用 10 System.out.println(Thread.currentThread().getName() +"------听音乐------" + i); 11 } 12 } 13 } 14 }
结论:继承和实现获取线程名称方式是不一样。
4、Thread方法(jion)
void join() 方法 :等待该线程终止。
1 /** 2 * JoinThread线程 3 */ 4 public class JoinThread extends Thread { 5 @Override 6 public void run() { 7 for (int i = 0; i < 100; i++) { 8 System.out.println("JoinThread --->" + i); 9 try{ 10 Thread.sleep(1); //JoinThread再休眠一秒,为了交替出现更有规律 11 } catch (InterruptedException e){ 12 e.printStackTrace(); 13 } 14 } 15 } 16 }
测试 join 方法:
1 /** 2 * 测试join方法 3 */ 4 public class JoinDemo { 5 public static void main(String[] args) throws InterruptedException { 6 JoinThread joinThread = new JoinThread(); 7 joinThread.start(); 8 9 for (int i = 0; i < 100; i++) { 10 System.out.println("main ---> " + i); 11 if(i == 10){ 12 joinThread.join(); //等待该线程终止,才继续执行main线程 13 } 14 Thread.sleep(1); //主线程模拟网络延迟 15 } 16 } 17 }
理解:joinThread.join(): 当主线程执行到10的时候,JionThread线程加进来,等待JionThread线程全部执行完,然后才继续执行主线程。
另外除了jion()方法以外,还有jion(long millils) 方法 (等待线程执行的固定时间,然后时间一到,jion失效)。
4、线程优先级
高优先级线程的执行优先于低优先级线程;并不是绝对的,可能优先级高的线程比优先级低的线程先执行,只能说,高优先级的线程优先执行的几率更多;
(比如两个线程,一个优先级高,一个优先级低,如果一共运行一个小时,优先级高的线程执行远远大于优先级低的,但是并不是说,优先级高的先执行完,在执行优先级低的)。
方法:
int getPriority() 返回线程的优先级
void setPriority() 更改线程的优先级
1 public class PriorityDemo { 2 public static void main(String[] args) { 3 Thread thread = Thread.currentThread(); 4 System.out.println(thread.getPriority()); //获取优先级 5 thread.setPriority(8); //设置优先级 6 System.out.println(thread.getPriority()); 7 } 8 }
自定义线程的默认优先级和创建它的环境的线程优先级一致。
5、守护线程
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
Thread类提供的方法:
方法: void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
方法:isDaemon() 测试该线程是否为守护线程。
1 public class DaemonDemo { 2 public static void main(String[] args) { 3 //获取主线程 4 Thread thread = Thread.currentThread(); 5 System.out.println(thread.isDaemon()); //false 非后台线程 6 //尝试变更为后台线程 7 thread.setDaemon(true); 8 //活动的线程不能设置后台线程,主线程不能设置为后台线程。 9 System.out.println(thread.isDaemon()); //Exception in thread "main" java.lang.IllegalThreadStateException 10 } 11 }
结论::自定义线程的默认状态和环境有关,后台线程中创建的线程默认是后台线程,前台线程中创建的线程为前台线程。
6、线程同步
Java中3种同步方式:
1、同步代码块
synchronized (同步监听对象){
需要同步的代码
}
注意事项: 同步监听对象 可以是任意的对象,但是多个线程来访问的,此对象必须是同一对象,一般写this,或者当前类的字节码对象(类名.class),或者相关的业务对象。
2、同步方法写法:
借助于方法的{} 把synchronized直接作为方法的修饰符;
3、同步方式-锁(lock)