java多线程学习一 基础
基础知识
进程
一个进程就是一个执行中程序,而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数据和状态都是完全独立的。
线程
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序控制的流程,但与进程不同的是:
同类滴多个线程是 共享一块内存空间和一组系统资源。 所以系统在各个线程之间切换时,开销要比进程小得多。所以线程好似 轻量级的进程。一个进程可以包含多个线程。
主线程
Java程序至少会有一个线程,这就是主线程,程序启动后是由 JVM创建主线程,程序结束是由JVM停止主线程。 主线程负责管理 子线程,即子线程的 启动、挂起、停止等等操作。
获取主线程 lang包内类
Thread mainThread = Thread.currentThread();
//打印线程名
System.out.println(mainThread.getName());
创建子线程
创建一个子线程涉及到 java.lang.Thread
类 和 java.lang.Runnable
接口。
Thread是线程类,创建一个Thread对象 就会产生一个 新的 线程。
而线程执行的程序代码是在 实现Runnable接口对象 的run() 方法中编写的,实现Runnable接口的 对象 是线程执行对象。
继承 Thread类创建线程
public class ThreadTest extends Thread{
public void run(){
for(int i=0;i<10;i++){
System.out.println("第"+i+"次 in "+Thread.currentThread().getName());
}
System.out.println(Thread.currentThread().getName()+" 执行完毕!");
}
}
public class TestThread {
public static void main(String[] args){
ThreadTest a=new ThreadTest();
a.setName("Tyrell");
a.start();
new ThreadTest().start();
new ThreadTest().start();
}
}
一旦线程启动后start方法就会立即返回,而不会等待到run方法执行完毕才返回。就好像run方法是在另外一个cpu上执行一样。
主线程由Java虚拟机负责,程序员负责启动自己的 线程。使线程 执行 需要调用Thread类中的 start()方法,start()方法 调用 被 覆盖的run()方法,完成线程功能的真正代码,放在类 的run()方法中,当一个类继承Thread类后,就可以在类中覆盖run()方法。然后 主方法中调用 start()方法,执行线程,也就是调用run()方法。如果不调用start()方法,线程永远也不会启动。 在主方法没有调用 start() 方法前,Thread对象只是一个实例,而不是一个真正的线程。
实现Runnable接口
如果需要继承其他类,那么就没有办法 通过继承Thread类 来使 当前类实现多线程了。Java不支持多继承,所以选择实现 Runnable接口。
`public class TTest extends Object implements Runnable
`
实质上 Thread类 实现了 Runnable接口 ,其中的run()方法 也正是对 Runnable接口 中run()方法的具体实现。实现Runnable接口的程序 会创建一个 Thread对象,并将Runnable对象 和 Thread对象相关联。Thread类中由两个构造方法:
```public Thread(Runnable target)public Thread(Runnable target,String name)
```
这儿还可以用匿名内部类 传入实现Runnable接口的匿名类,如:
Thread t=new Thread(new Runnable(){
public void run(){
}
//
},"myThread");
//可以省略线程名 myThreadt.start();
## 线程的生命周期
线程具有生命周期,其中包含7种状态,分别是
出生状态:线程被创建时处于的状态,在用户使用该线程实例 调用 start()方法前,都处于出生状态。
就绪状态:用户调用start()方法 后,线程处于就绪状态。
运行状态:当线程得到系统资源后, 就进入运行状态。
等待状态:当处于 运行状态的线程 调用 Thread类的wait()方法时,该线程进入等待状态。必须调用Thread类中的notify()方法 唤醒。notifyAll()方法是将所有处于等待状态的线程唤醒。
休眠状态
阻塞状态
死亡状态
操作线程的方法
线程的休眠
线程休眠有可能抛出 InterruptedException(打扰,插嘴),所以sleep方法的调用要放到try{}catch(){} 块中。 虽然使用了 sleep()方法的线程在一段时间后 会醒来,但是并不能保证醒来后进入 运行状态,只能保证它进入就绪状态。```public void run(){ // Thread.sleep(999);//单位毫秒 秒/1000}```
### 线程的加入 如果当前程序为 多线程 程序。加入已经存在一个线程A,现在需要插入线程B。当某个线程 加入到零一个线程时,令一个线程会等待 该线程结束后 在继续执行。```//join():加入运行class Test implements Runnable{ public void run(){ for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+"--->"+i); } }}class Demo{ public static void main(String[] args) throws InterruptedException{ Test test = new Test(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start();//t1 start之后不一定能抢到CPU //写在这里,加入下面这句话之后,这时只有主线程和t1线程,所以主线程会等待t1线程全部执行完再执行 //t1.join();//让t1线程加入线程,如果没有这句话,t1线程不一定能得到CPU t2.start(); t1.join();//写在这里,这时有主线程和t1,t2线程,主线程会等待t1线程执行完,t2线程不会让着t1 //join只有主线程会让着他 for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+"--->"+i); } }}```
### 线程的中断
### 线程的礼让 Thread类中提供了一种礼让方法,使用yield()方法表示。[e屈服,让步],它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其它线程,这仅仅时一种暗示,没有机制保证一定会礼让; yield()方法使具有相同优先级的线程 有进入 可执行状态 的机会,当 当前线程放弃执行权时会再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用yield()方法,操作系统会为线程自动分配CPU时间片 来执行。
## 线程的优先级线程的优先级 可以表明在 程序中 该线程的重要性,系统根据优先级决定运行那个线程。
可以用`thread.setProperty(优先级)方法调整``优先级 为 1--10默认优先级为 5`
## 线程同步 | 线程安全 线程同步机制
#### 同步块```public class Runner implements Runnable{ int num=10; int i=15; @Override public void run(){ while(i>0){ synchronized(""){ if(num>0){ try{ Thread.sleep(100); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("tickets "+num+" in"+Thread.currentThread().getName()); num--; } } i--; } } public static void main(String[] args){ Runner r = new Runner(); Thread t1=new Thread(r,"A"); Thread t2=new Thread(r,"B"); Thread t3=new Thread(r,"C"); t1.start(); t2.start(); t3.start(); }}``` 将资源放置在 同步块中。这个同步块也称为临界区,它使用 synchronized 关键字建立。```synchronized(Object){//}``` 通常将 共享资源 的操作放置在 synchronized 定义的区域内,这样当其他线程也获取到这个锁 时,必须等待 锁 被释放时 才能进入 该区域。----------
#### 同步方法```synchronized void function(){ //}```