java——线程总结

进程和线程的区别

进程是一个应用程序,线程是进程的一部分。进程是由多个线程组成的。
进程与进程之间相互独立,互不影响,资源不共享。
而线程之间,栈内存相互独立(一个栈是一个线程),但堆内存和方法区共享。如下图:在这里插入图片描述

创建线程的两种方式

第一种:编写一个类,直接继承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模拟生产者消费者模式:
生产者消费者模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值