这里写目录标题
进程和线程的区别
进程是一个应用程序,线程是进程的一部分。进程是由多个线程组成的。
进程与进程之间相互独立,互不影响,资源不共享。
而线程之间,栈内存相互独立(一个栈是一个线程),但堆内存和方法区共享。如下图:
创建线程的两种方式
第一种:编写一个类,直接继承java.lang.Thread,然后重写run方法,在run方法中写线程执行的内容。要调用线程,就要调用线程对象的start方法。
public class 线程 {
public static void main(String[] args) {
Thr t=new Thr();
t.start();
for(int i=0;i<50;i++)
System.out.println(2);
}
}
class Thr extends Thread{
public void run() {
for(int i=0;i<50;i++)
System.out.println(1);
}
}
上述代码中:t.start()方法的任务是开辟一个栈内存区,存入run方法帧,该栈与main方法栈各自执行,互不干扰。
第二种: 编写一个类,实现Runnable接口,然后用Thread类创建对象,传入实现Runnable的类的对象。此时,该线程对象的run方法是实现Runnable接口类中的run方法。注意: 实现Runnable接口后会要求实现给接口中的抽象方法run
使用接口比继承Thread用的要多,因为如果继承Thread类,该类就不能再继承其他的类。
第三种采用匿名内部类的方式,如下列代码:
Thread th=new Thread(new Runnable() {
public void run() {
for(int i=0;i<50;i++)
System.out.println(1);
}
});
th.start();
for(int i=0;i<50;i++)
System.out.println(2);
线程的生命周期
线程对象的名字
创建一个线程对象后,默认的线程名字为Thread-0或Thread-1等累加下去,可以通过方法setName设置线程的名字,还可以通过getName方法获取线程名字,返回String类型变量。
获取当前线程对象
在某个线程中执行Thread th=Thread.currentThread()可以获取当前线程对象。currentThread是Thread类中一个静态方法。
sleep方法和interrupt方法
1、sleep方法返回值未空,可以传入一个long型参数,表示当前线程休眠的时间,单位是毫秒,执行sleep方法后,当前线程进行了阻塞态。
注意:sleep方法是一个静态方法,如果用对象进行调用,与调用该方法的对象无关,sleep方法出现在哪个线程就让哪个线程休眠,如下列代码:
public class 线程 {
public static void main(String[] args) {
Thr t=new Thr();
t.start();
try {
t.sleep(5*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(t.getName());
}
}
class Thr extends Thread{
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println(1);
}
}
}
以上程序先打印for循环的50个数字,5秒之后才执行t.getName(),即t.sleep()是让main方法主线程休眠,因为该方法的调用是出现在主线程中。
2、interrupt方法是让正在睡眠的线程结束随眠,从阻塞态进入就绪态,如下列代码:
public class 线程 {
public static void main(String[] args) {
Thr t=new Thr();
t.start();
for(int i=0;i<10;i++)
System.out.println("main");
t.interrupt();
}
}
class Thr extends Thread{
@Override
public void run() throws RuntimeException{
try {
Thread.sleep(5*1000*444*555);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<50;i++)
System.out.println(1);
}
}
以上代码在输出10次mai后,线程Thr被唤醒,输出50次1,。
interrupt方法的原理是导致sleep方法出现异常,进入catch语句,从而结束睡眠,以上代码执行interrupt方法后,会执行e.printStackTrace();打印异常信息,才开始执行输出50个1.
线程的终止
1、stop方法(已过时),该方法会使线程消亡,会导致线程中未保存的数据丢失,故不再使用该方法。
2、标记法,在线程类中定义一个布尔变量flag,默认值为true,在run方法中,使用if_else语句,结构如下
class myRun implements Runnable{
boolean flag=true;
public void run() {
if(flag) {
//线程内容
}else {
//此处保存数据
return;
}
}
}
}
要结束线程,只需将flag值该为false,就可以进入else语句块,保存数据后,结束run方法。如下列例子:
public class 线程终止 {
public static void main(String[] args) {
myRun myRunable=new myRun();
Thread th=new Thread(myRunable);
th.start();
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
myRunable.flag=false;
}
}
class myRun implements Runnable{
boolean flag=true;
public void run() {
for(int i=0;i<30;i++) {
if(flag) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println("线程结束,保存数据");
return;
}
}
}
}
主线程休眠5秒后,让th线程执行了5秒,输出了5次,然后主线程执行myRunable.flag=false;结束了th线程。
线程调度
线程优先级
线程优先级是线抢占CPU的能力指标,多个线程并发时,优先级越高的线程抢占的cpu时间片更多。
最低优先级是1
最高优先级是10
默认优先级是5
优先级可以通过setsetPriority(int pri)来设置线程的优先级,通过getPriority()来获取线程的优先级(返回值为int)。
线程让位
可以通过调用yield来使当前线程结束CPU占用,进入就绪态。
线程插入
在一个线程中,插入一个线程,可以使用join方法,如下列代码:
public class join方法 {
public static void main(String[] args) {
Thread th=new Thread(new Run2());
th.start();
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<1000;i++)
System.out.println("main");
}
}
class Run3 implements Runnable{
public void run() {
for(int i=0;i<1000;i++)
System.out.println("Thread2");
}
}
以上代码中,在主线程插入了线程th,当th执行结束后,才会继续执行后面的代码。即输入1000次Thread2后才能开始输出main。
注意:join方法执行后,th线程的栈内存依然存在。
线程安全(重点)
守护线程
java中的线程分为两类:用户线程、守护线程(后台线程)
具有代表性的守护线程是:垃圾回收线程。
守护线程的特点:一般守护线程是一个死循环,所有用户线程结束,守护线程就自动结束。
注意:主线程main方法是一个用户线程。
实现线程的第三种方式
FutureTask方式,实现 callable接口(JDK8新特性),这种方式可以获得线程的返回值。 而继承Thread和实现接口无法获取返回值,因为run方法返回值为void。
如下列代码:
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask f=new FutureTask(new Callable() {
public Object call() throws Exception {
System.out.println("call method begin");
Thread.sleep(1000*5);
System.out.println("call method end");
int a=5,b=10;
return a+b;
}
});
Thread t=new Thread(f);
t.start();
//get方法会导致当前线程的阻塞
Object o=f.get();
System.out.println(o);//输出结果为15
}
这种方法有一个缺点:使用get方法会导致当前线程阻塞,要直到线程t结束才能继续执行。
Object类中的wait方法和notify方法(生产者和消费者模式)
wait和notify方法是java中任何一个对象都有的方法,因为这两个方法是object类中自带的
wait和notify方法的用法:
Object o=new Object();
o.wait();//表示让当前线程停下(使用对象o的线程停下)
o.notify();//表示让使用对象o的线程继续执行
还有一个notifyAll方法:表示唤醒所有使用o的线程
java模拟生产者消费者模式:
生产者消费者模式