多线程的理解
多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并 发执行,每一个任务就是一个线程。这就是多线程程序。
在java中有四种方式实现多线程
Thead类的常用方法
void start();启动线程,并执行对象的run()方法。
run();线程在被调度时执行的方法。
String getName();返回线程的名称。
void setName(String name);设置线程的名称。
static Thread currentThread();返回当前线程。在Thread类子类中 就是this,通常用于主线程和Runnable实现类。
static native void yield();线程让步,暂停当前执行的线程,把执行机会让给优先级高的线程。
void join();加入一个线程,调用线程被阻塞,直到join()方法加入的join线程执行完。
static void sleep(long millis, int nanos);//当前线程放弃cpu的执行权,是其他线程有机会得到执行权,睡眠结束后重新排队。
void stop();强制线程生命周期结束。不推荐使用。
boolean isAlive();判断线程是否还活着。
线程的优先级
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;默认的优先级
public final static int MAX_PRIORITY = 10;
int getPriority();//得到当前线程的优先级。
void setPriority();//设置当前线程的优先级。
一、方式一: 继承于Thread类
实现步骤:
1、创建一个继承于Thread类的子类
2、重写Thread类的run()方法
3、创建Thread类的子类的对象
4、通过此对象调用start()
例子:创建一个新线程遍历100以内所有的偶数,主线程遍历100以内的奇数
创建一个继承于Thread类的子类
class MyThread extends Thread{
//重写Thread类的run方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------MyThread线程---------"+i);
}
}
}
}
创建Thread类的子类的对象
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 100; i++) {
if (i % 2 == 1){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------main线程---------"+i);
}
}
}
}
说明:由于执行速度太快会导致主线程把所有输出后,新线程才抢到cpu执行权,所有在输出前让线程睡眠一毫秒。
结果:
注意点:
问题1:我们不能通过直接调用run()的方式启动线程。否则没有开启一个线程,仍然是主线程执行。
问题2:一个线程只能start一次,如果start一个已经运行的线程会报IllegalThreadStateException异常
源码
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
二、方式二: 实现Runnable接口
实现步骤:
1、创建一个实现了Runnable接口的类。
2、实现类去实现Runnable中的抽象run()方法。
3、创建实现类的对象。
4、将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象。
5、通过Thread类的对象调用start()方法。
例子:多窗口买票,总票数为100张票。
实现了Runnable接口的类
class Window implements Runnable{
private int tikit = 100;
@Override
public void run() {
while (true){
try {
if (tikit > 0) {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ":" + tikit);
tikit--;
} else
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
创建runnable实现类的对象
public static void main(String[] args) {
Window w1 = new Window();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.start();
t2.start();
t3.start();
}
运行结果:
说明:存在线程安全问题,后面会有线程安全问题解决方法。
三、比较前两种创建线程的方式
开发中,优先选择实现Runnable接口的方式
原因1:单继承的局限性。
原因2:实现的方式更适合来处理多线程处理共享数据的情况。(例:多窗口买票中的票)
四、方式三:实现Callable接口(JDK5.0新增)
实现步骤:
1、创建一个实现了Callable接口的类。
2、实现类去实现Callable中的抽象call()方法。
3、创建实现类的对象。
4、创建FutureTask对象,并把实现类的对象作为参数传递到FutureTask类中的构造器中。
5、创建Thread类的对象,并把FutureTask类的对象作为参数传递到Thread类中的构造器中。
5、通过Thread类的对象调用start()方法。
6、获取Callable中的call()方法的返回值。
Future接口:可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
FutureTask是Future接口的唯一实现的类。
FutureTask同时实现了Runnable、Futrue接口,它既可以作为Runnable被线程执行,又可以作为Futrue得到Callable的返回值。
例子:输出100以内的偶数,并把偶数的和返回
Callable的实现类:
class NumThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <=100; i++) {
if (i%2==0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public static void main(String[] args) {
NumThread numThread = new NumThread();
FutureTask<Integer> futureTask = new FutureTask(numThread);
Thread thread = new Thread(futureTask);
thread.start();
try {
//get()方法的返回值为FutrueTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
与使用Runnable相比,Callable功能更强大些
1、相比run()方法,可以有返回值。
2、方法可以抛出异常。
3、支持泛型的返回值。
4、需要借助FutrueTask类,比如获取返回结果。
五、方式四:使用线程池(JDK5.0新增)
背景:经常创建和销毁、使用量特别大的资源,比如高并发情况下的线程,对性能影响很大。
思路:提前创建多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建线程、实现重复利用。
好处:
1、提高响应速度(减少了创建线程的时间)。
2、降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
3、便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止。
JDK5.0起提供了线程池相关的API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。 Executors.newFixedThreadPool(int num);
实现步骤:
1、提供指定线程数量的线程池
2、执行指定的线程的操作。需要提供实现Runnable或Callable接口实现类的对象
3、关闭线程池。
例:实现Runnable输出100以内的奇数,实现Callable输出100以内的偶数
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumThread2 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <=100; i++) {
if (i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);
sum += i;
}
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor service = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//设置线程池的属性
service.setCorePoolSize(10);
service.execute(new NumberThread());//适合使用于Runnable接口
Future<Integer> future = service.submit(new NumThread2());//适合使用Callable接口
//得到返回值
Integer num = future.get();
System.out.println(num);
ThreadPoolExecutor pool = (ThreadPoolExecutor) service;
pool.setCorePoolSize(15);
// pool.setKeepAliveTime();
//关闭线程池
service.shutdown();
}