转载请注明出处:http://blog.csdn.net/github_39430101/article/details/77340996
创建线程的三种方法
java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的的定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
继承Thread类
public class TestThread extends Thread{
@Override
public void run(){
for(int i=0; i<100; i++){
System.out.println("我是线程");
}
}
}
创建和启动线程:
public class ThreadApp{
public static void main(String[] args){
Thread thread = new TestThread(); //实例化线程
thread.start(); //启动线程
}
}
实现Runnable创建并启动线程
实现Runnable接口并重写run()方法来定义线程体,然后再创建线程的时候讲Runnable的的实例传入并启动线程。这样做的好处在于可以将线程与线程要执行的任务分离开减少耦合,同时Java是单继承的,定义一个类实现Runnable接口这样的做法可以更好的去实现其他父类或接口。因为接口是多继承关系。这里我们讲一下静态代理设计模式。
静态代理
- 真实角色
- 代理角色 :持有真实角色的引用
- 二者实现相同的接口
public class StaticProxy {
public static void main(String[] args) {
//创建真实角色
You you = new You();
//创建代理角色+真实角色的引用
Agency agency = new Agency(you);
agency.renting();
}
}
//创建一个租房的接口,里面有租房方法
interface Rent{
//公共方法租房
void renting();
}
//真实角色
class You implements Rent{
@Override
//租房
public void renting(){
System.out.println("我租房子");
}
}
//代理角色,中介公司
class Agency implements Rent{
Rent you;
public Agency(){
}
public Agency(Rent you){
this.you = you;
}
//租房前要做的
private void before(){
System.out.println("找中介公司");
}
private void after(){
System.out.println("交房租");
}
@Override
public void renting(){
before();
you.renting();
after();
}
}
//输出:
找中介公司
我租房子
交房租
Runnable相当于我们的租房接口,Thread类也是实现它的,我们只需要实现它并重写run()方法,其他的事情交给中介Thread类
//真实角色类
public class TestRunnable implements Runnable{
@Override
public void run(){
for(int i=0; i<100; i++){
System.out.println("我是线程");
}
}
}
启动线程
public class ThreadApp{
public static void main(String[] args){
Runnable runnable = new TestRunnable();
Thread thread = new Thread(runnable); //实例化线程并传入线程体
thread.start(); //启动线程
}
}
实现Callable接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。Callable和Runnable有几点不同:
- Callable规定的方法是call(),而Runnable规定的方法是run()
- call()方法可抛出异常,而run()方法是不能抛出异常的
- Callable的任务执行后可返回值,运行Callable任务可拿到一个Future对象,而Runnable的任务是不能返回值的。Future标识异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
public class Call {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程API
Thread的静态方法currentThread方法可以用于获取运行当前代码片段的线程。
Thread current = Thread.currentThread();
获取线程信息
long getId() //返回该线程的标识符
String getName() //返回该线程的名称
int getPriority() //返回线程的优先级
Thread.state getState() //获取线程的状态
boolean isAlive() //测试线程是否处于活动状态
boolean isDaemon() //测试线程是否为守护线程
boolean isInterrupted() //测试线程是否已经中断
常用方法:
void run() //创建该类的子类时必须实现的方法
void start() //开启线程的方法
static void sleep(long t) //释放CPU的执行权,不释放锁。调用的目的是不让当前线程霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会
final void wait() //释放CPU的执行权,释放锁。当一个线程执行到wait()方法是,它就进入到一个和该对象相等的等待池(Waiting Pool)中,同时失去了对象的锁-暂时的,wait后还要返还对象锁。当前线程必须拥有对前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用
final void notify()/notifyAll() //唤醒在当前对象等待池中等待的第一个线程/所有线程。notify/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常
static void yield() //该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
void join() //该方法用于等待当前线程结束,是一个阻塞方法。
线程的状态
- 新建状态(new):当线程对象创建后,即进入了新建状态,如:Thread t = new MyThread();
- 就绪状态(Runnable):当调用线程对象的start()方法线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了等待CPU调度分配时间片的准备,并不是说执行了start()方法此线程就立即会执行。
- 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此线程才开始真正执行,即进入到运行状态。
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会再次被CPU调用。阻塞状态可分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态
2.同步阻塞:线程在获取synchronized同步锁(锁被其他线程所占用),它会进入同步阻塞状态
3.其他阻塞:通过调用线程的sleep()或join()或发出了IO请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、后者IO处理完毕时,线程重新转入就绪状态。- 死亡状态(Dead):线程执行完了或者因为异常退出了run()方法,该线程结束生命周期
停止线程
- 自然终止:线程体正常执行完毕
- 外部干涉
1.线程类中定义线程体使用的标识
2.线程体使用该标识
3.提供对外的方法改变该标识
4.外部根据条件调用该方法
public class Demo {
public static void main(String[] args) {
Study s = new Study();
new Thread(s).start();
for(int i=0; i<100; i++){
if(i == 50){
s.stop();
}
System.out.println("main...."+i);
}
}
}
class Study implements Runnable{
private boolean flag = true;
@Override
public void run(){
while(flag){
System.out.println("study thread...");
}
}
public void stop(){
this.flag = false;
}
}
如果线程是阻塞的,则不能使用上面的方法来终止线程
public class ThreadInterrupt extends Thread {
public void run() {
try {
sleep(50000); // 延迟50秒
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) throws Exception {
Thread thread = new ThreadInterrupt();
thread.start();
System.out.println("在50秒之内按任意键中断线程!");
System.in.read();
thread.interrupt();
thread.join();
System.out.println("线程已经退出!");
}
}
线程阻塞
join
public class Demo extends Thread{
public static void main(String[] args) {
Demo demo = new Demo();
Thread t = new Thread(demo);
t.start();
for(int i=0;i<1000;i++){
if(i == 50){
try {
t.join(); //main阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main............."+i);
}
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("join....."+i);
}
}
}
当main执行到49的时候开始阻塞了,执行join。
yield
public class Demo extends Thread {
public static void main(String[] args) {
Demo demo = new Demo();
Thread t = new Thread(demo);
t.start();
for(int i=0;i<1000;i++){
if(i % 20 ==0){
Thread.yield(); //暂停本线程
}
System.out.println("main............."+i);
}
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("join....."+i);
}
}
}
sleep
休眠,不释放锁。常用于
- 与时间相关:倒计时
- 模拟网络延时
倒计时
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* 倒计时
* 倒数10个数,一秒内打印一个
*/
public class Demo extends Thread {
public static void main(String[] args) throws InterruptedException {
Date endTime = new Date(System.currentTimeMillis()+10*1000);
System.out.println(endTime);
long end = endTime.getTime();
while(true){
//输出
System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
//等待一秒
Thread.sleep(1000);
//构建下一秒的时间
endTime = new Date(endTime.getTime()-1000);
//10秒内继续,否则退出
if((end-10000)>endTime.getTime()){
break;
}
}
}
}