线程的特点:
1、抢占式线程
- 效率高
- 可防止单一线程长时间占用cpu
2、在单核cpu中,宏观上同时执行,微观上顺序执行。
线程的状态:
初始状态–就绪状态–运行状态–终止状态
此外还有限期等待(sleep(n))–无限期等待join()–阻塞状态(synchronized)
请看下图:
但是在JDK5之后,就绪状态和运行状态统称Runnable
具体的状态我们可以通过getState()方法看到。
public enum State {
/**
* Thread state for a thread which has not yet started.
* 线程刚创建好之后的初始状态
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
* 线程的运行状态,或者等待时间片选中的状态为Runnable
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
* 未获得锁的状态
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
* 调用join方法后其他线程进入waitting状态
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
* 调用sleep方法后其他线程进入timed_waitting状态
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
* 线程终止状态
*/
TERMINATED;
}
创建线程的三种方式:
- 1、继承Thread类,重写run方法
- 2、实现Runnable接口
- 3、实现Callable接口
1、继承Thread类,重写run方法
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
int i =100;
for (int i1 = 0; i1 < i; i1++) {
System.out.println("子线程"+i1+" 线程Name: "+ this.getName()+" :线程ID : "+this.getId());
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread("nxw");
myThread.start();
MyThread myThread1 = new MyThread("silence");
myThread1.start();
for (int i=0;i<100;i++){
System.out.println("主线程:"+i);
}
}
}
在MyThread 类中有一个main方法,在执行main方法的时候会开启一个主线程来执行,在执行过程中有开启了子线程来执行myThread.
从以上代码中执行结果会出现主线程的子线程交替打印的情况
但是如果你想先执行myThread,在执行myThread1,最后打印主线程的话,你只需要加上这样一段代码就可以了
myThread.join();
myThread1.join();
注意这时候就需要抛异常了throws InterruptedException
2、实现Runnable接口
public class MyRunnable implements Runnable {
public synchronized void run() {
for (int i = 0;i<101;i++){
System.out.println("子线程ID:"+Thread.currentThread().getId()+"子线程Name:"+Thread.currentThread().getName()
+"子线程:"+i);
}
}
}
class TestMyRunnable{
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable,"runnable: ");
Thread thread1 = new Thread(myRunnable,"runnable1: ");
thread.start();
thread1.start();
for (int i = 0;i<101;i++){
System.out.println("主线程:"+i);
}
}
}
3、实现Callable接口
public class TestCallable {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
int sum = 0;
for(int i=0;i<101;i++){
sum += i;
}
return sum;
}
};
FutureTask task = new FutureTask(callable);
Thread thread = new Thread(task);
Thread thread1 = new Thread();
thread.start();
Object o = null;
try {
o = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(o);
//打印结果5050
}
}
以上就是实现多线程的三种方式,但是要着重说一下实现Callable这种方式。
根据代码可以看出来,实现Callable后不能直接把对象给Thread对象,而是要经过FutureTask 包装后才能交给Thread。再仔细一点你就会发现实现Callable接口还需要抛出异常。
通过以上代码我们可以总结一下三种创建线程方式的区别:
三种创建线程方式的区别
1、实现线程方式大体可分为两类,一类是继承,一类是接口实现。
2、接口实现可以避免单继承的局限性。
3、Runnable要实现的方法是run()方法,Callable()要实现的方法是call()方法。
4、Callable任务执行完毕后会有返回值,而Runnable是没有返回值的。
5、call()方法需要抛异常,而run()方法不需要抛异常。
6、Callable对象需要交给FutureTask对象包装后才能交给Thread开启线程,而Runnable可以直接交给Thread。
7、Callable执行完毕后可以通过futureTask获取线程执行结果。