您的位置:首页 > 编程语言 > Java开发

Java多线程系列(一)—多线程基础

2017-10-13 18:13 330 查看

Java多线程系列(一)—多线程基础

线程是CPU调度的最小单元,单核时代多线程可以在IO密集型操作时有更高的效率,在如今这个多核为主的时代,多线程更能有效的发挥多核的优势,充分利用CPU资源;

个人主页:tuzhenyu’s page

原文地址:Java多线程系列(一)—多线程基础

1. 线程基础

(1) 线程和进程

进程是系统资源分配的基本单位,线程是CPU资源调度的最小单元,一个进程下可以有多个线程;

每个进程都有各自独立的地址空间,所有进程共享父进程的地址空间;

进程上下文切换的开销远远大于线程的上下文切换

(2) 线程的创建

调用Java程序入口的public void static main(){}的是一个由JVM默认创建的名称叫做main的线程

继承Tread类创建线程,线程的启动需要通过Thread.start()调用,会通过本独方法的调用让操作系分配CPU资源,JVM中的Thread对象只是线程的外壳;

Public class MyThread extends Tread{

@override

pulic void run(){

super.run();    //继承Thread类的run()方法

}

}

Public class Run {

public static void main(){

MyThread myThread = new  MyThread();

myThread.start();

}

}


实现Runnabale接口创建线程,实现Runnable的实例化对象作为参数传入Thread中设置到一个名为”target”的属性上,Thread默认的Run是调用这个target的run()方法来完成;

Public class MyRunnable implements Runnable{

pulic void run(){

System.out.println("hello world")

}

}

Public class Run {

public static void main(){

MyRunnable myRunnable = new MyRunnable();

MyThread myThread = new  MyThread(myRunnable);

myThread.start();

}

}


通过Thead创建线程和通过Runable创建线程区别

通过Thead创建线程需要继承Thread类重写run()方法,线程运行时直接执行继承类的run方法;

通过Runnable创建线程需要实现Runnable接口,并将实现类当做参数注入到Thread类的target中,线程运行时会运行Thread类的run()方法查看对应target是否为空,如果不为空则调用target的run()方法;

@Override
public void run() {
if (target != null) {
target.run();
}
}


- Thread实现只能继承一个类,Runnable接口可以实现多个接口,在需要多继承的情境下使用Runnable接口实现


(3) 线程的状态

NEW(新建状态):new了一个新的线程对象,还未调用start()

Runnable(可运行状态):包含Ready就绪状态和Running运行状态

就绪状态:其他线程调用了该对象的start()方法,等待获取CPU的使用权

运行状态:获取了CPU的使用权,执行run()程序

Block(阻塞状态):通常是因为等待锁造成线程挂起,JVM会把该线程置为阻塞状态

Waiting(等待状态):执行Object.wait()方法后所处的等待状态,需要其他线程通知或者中断

Time_Waiting(超时等待状态):执行Thread.sleep(n)线程睡眠挂起,进入超时等待状态;

Terminated(终止态):执行完run()方法后线程的状态;

(4) 线程的优先级

Java中优先级分为1~10等级,默认main的等级为5

线程优先级具有继承性,若B的优先级未明确设置,A线程启动B线程,则B线程的优先级与A是一样的

2. 线程常用方法

(1) Thread类的常用方法

静态方法

Thread.currentThread()方法:获取当前线程

Thread.currentThread().getName()    // 获取线程名

Thread.currentThread().getId()    // 获取线程唯一标识


sleep():让该正在执行的线程休眠指定毫秒数

this.currentThread().sleap(2000)    //休眠2秒


yield()线程让步

Thread.yield();    //当前线程进行让步,放弃CPU资源后重新竞争,如果竞争失败则从运行态度转换为阻塞态;


intercepted(),isIntercepted()线程停止判断

实例方法

start():启动线程,进入就绪状态等待获取CPU资源

run():mian线程调用Thread.run()方法,代码还是同步顺序执行

isAlive()方法:判断当前的线程是否处于活动状态(就绪状态,运行状态)

myThread.isAlive()    //判断线程是否处于存活状态


intercept()线程停止

myThread.intercept();    //将myThread线程标记为停止态


join()线程等待

myThread.join();    //当前线程等待myThread线程执行完后再执行


(2) 线程的停止

线程停止的方法:

当run()方法完成后线程终止

使用Thread.stop()方法强行终止当前线程

使用Thread.interrupt()方法中断当前线程

interrupt()+interruptted+break

interrupt()+interruptted+return

interrupt()+interruptted+throw Exception

sleep()和wait()方法+interrupt()抛出InterruptedException异常

class MyThread extends Thread {
@Override
public void run() {
System.out.println("run:"+ System.currentTimeMillis());
super.run();
for (int i=0;i<200;i++){
if(this.interruptted){
break;
}
System.out.println("i:"+i);
}

}
}
public class ThreadStopTest {
public static void main(String[] args) throws InterruptedException{
MyThread myThread = new MyThread();
myThread.start();
System.out.println("start:"+System.currentTimeMillis());
Thread.sleep(2000);    //main主线程休眠,让出CPU使用权给myThread线程,不然会执行完main再让出使用权
System.out.println("sleep:"+System.currentTimeMillis());
myThread.interrupt();
System.out.println("interrupt:"+System.currentTimeMillis());

}
}


判断线程停止状态:

this.interrupted():测试当前线程是否已经是中断状态,执行后清除状态标志

this.isInterrupted():测试当先线程对象是否已经中断,但不清除状态标志

suspend()和resume():可能会出现独占公共同步对象,也会出现因为暂停造成的不同步现象

(3) 线程的让步

线程的让步可以通过yield(),sleep(0),sleep(1)实现

sleep(0),sleep(1)和yelid()实现线程让步区别

sleep(0)线程进入就绪状态,但是只允许优先级更高的线程使用CPU,如果没有合适的线程该线程会重新获取到时间片

sleep(1)线程睡眠1ms后进入就绪状态,各个线程公平抢占CPU

yelid()线程让出CPU进入就绪状态,并且和其他线程公平竞争CPU

线程让步Thread.yield()是通过本地方法(native)实现的

(4) 线程的等待

Thread.join()的使用:一个线程A执行了thread.join()表式线程A要等待线程thread运行结束后才能从thread.join()中返回继续执行后面的代码。

//全部开启线程后执行线程等待

public static void main(String[] args) throws Exception{
Thread[] threads = new Thread[5];
for (int i=0;i<5;i++){
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId()+" is run");
}
});
}
for (int i=0;i<5;i++){
threads[i].start();
}
for (int i=0;i<5;i++){
threads[i].join();
}
System.out.println("finish");
}


线程等待join()方法实现原理

join()方法加有对象锁,锁的对象就是被等待的线程;在方法内循环判断被等待线程是否存活,如果存活则调用wait()方法进入阻塞状态等待被通知;

当被等待线程结束时会做清理工作,其中包括一项就是通知唤醒等待该线程对象锁的线程;因此join()方法是通过对象锁实现的;

public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}


(4) 线程的wait/notify通信机制

class Thread1 extends Thread{
private Object object;

public Thread1(Object object){
this.object = object;
}

@Override
public void run() {
try{
synchronized (object){
System.out.println("begin to wait:"+Thread.currentThread().getName());
object.wait();
System.out.println("wait end:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Thread2 extends Thread{
private Object object;

public Thread2(Object object){
this.object = object;
}

@Override
public void run() {
synchronized (object){
System.out.println("begin to notify:"+Thread.currentThread().getName());
object.notify();
System.out.println("notify end:"+Thread.currentThread().getName());
}
}
}
public class WaitNotifyTest {
public static void main(String[] args) throws InterruptedException{
Object o = new Object();
Thread1 thread1 = new Thread1(o);
thread1.start();
Thread.sleep(2000);
Thread2 thread2 = new Thread2(o);
thread2.start();
}
}


wait()和notify()必须在同步块中调用,wait()和notify()必须持有同一对象锁,这样才能wait()释放对象锁后notify()持有该对象锁;wait()应该先持有对象锁,执行wait()方法后释放锁,其他线程竞争该对象锁;

调用wait()的线程将进入Waiting状态,只有等待其他线程通知或者被中断才返回;wait()方法底层调用wait(0)表示一直等待直到被其他线程唤醒;

wait(long)方法:等待某一时间内是否有线程进行唤醒,超出这个时间会自动唤醒,如果未竞争得到CPU资源则进入阻塞Blocked状态;

notify()方法用来通知wait状态的线程,如果有多个持有相同对象锁的线程,随机挑选一个发出通知notify

执行完notify()方法之后,当前线程不会马上释放该对象锁,要执行完synchronized代码块内的程序后才能释放

notifyAll()方法:唤醒所有处于当前对象锁的wait状态的线程

总结

线程的方法一般是通过调用本地方法将具体操作交由操作系统执行,线程让步join()方法的实现是通过对象锁;

a3ef
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: