实现线程操作的三种方法
1-1 进程
-
程序是静止的,运行中的程序就是进程。
-
进程的三个特征:
- 动态性 : 进程是运行中的程序,要动态的占用内存,CPU和网络等资源。
- 独立性 : 进程与进程之间是相关独立的,彼此有自己的独立内存区域。
- 并发性 : 假如CPU是单核,同一个时刻其实内存中只有一个进程在被执行。 CPU会分时轮询切换依次为每个进程服务,因为切换的速度非快,给我们的感觉这些进程在同时执行,这就是并发性。
- 并行:同一个时刻同时在执行。
1-2 线程
- 线程是属于进程的。一个进程可以包含多个线程,这就是多线程。线程创建开销相对于进程来说比较小。 线程也支持并发性。
- 线程的作用: 可以提高程序的效率,可以有更多机会得到CPU。
1-3 进程和线程的总结
-
进程是运行中的程序。有动态性,独立性,并发性。一个进程可以包含多个线程,线程也支持并发。
-
多线程可以提高程序的整体效率,可以解决很多业务需求。因为多个线程是并发抢占CPU执行,所以多线程的执行会出现随机性。
2-创建线程的三种方式
方式1:定义一个线程类继承Thread类,然后重写run()方法,再创建线程对象,调用start()方法启动线程
/**
方式一的步骤:
a.定义一个线程类继承Thread类
b.重写Thread类的run()方法
c.创建一个子线程对象
d.调用线程对象的start()方法启动线程(其实最终就是执行线程对象的run()方法)
线程的注意:
1.启动线程不能直接调用run()方法,否则是普通对象的普通方法调用了,将失去线程特征。线程的启动必须调用start()
2.一般是先创建子线程,再申明主线程的任务,否则主线程任务总是先执行完!
优缺点:
优点:编码简单。
缺点:线程类已经继承了Thread类,不能再继承其他类,功能被削弱了。不能做线程池。无法直接返回线程执行的结果。
*/
public class ThreadDemo {
// 自带一个线程:main方法本身就是一个线程,是最牛逼的线程!
public static void main(String[] args) {
// c.创建一个子线程对象
MyThread t = new MyThread();
// d.启动线程
// 注意:启动线程不能直接调用run()方法,否则是普通对象的普通方法调用了,将失去线程特征。
// 线程的启动必须调用start()
t.start();
for(int i = 0 ; i < 10; i++){
System.out.println("main线程执行:"+i);
}
}
}
// 线程类不是线程对象,是用来创建线程对象的。
// a.定义一个线程类继承Thread类
class MyThread extends Thread{
// b.重写Thread类的run()方法
@Override
public void run() {
for(int i = 0 ; i < 10; i++){
System.out.println("子线程执行:"+i);
}
}
}
方式2:定义一个线程任务类实现Runnable接口,然后重写run()方法。创建线程任务对象,把线程任务对象包装成线程对象,调用start()方法启动线程
/**
a.定义一个线程任务类实现Runnable接口。重写run()方法
b.创建一个线程任务对象
c.把线程任务对象包装成一个线程对象
-- public Thread(Runnable target)
d.调用线程对象的start()方法启动线程。
优缺点:
缺点:编程相对复杂,不能直接返回线程的执行结果
优点:
1. 一个任务对象可以被反复包装成多个线程对象。
2. 可以避免java中的单继承的局限性。因为线程任务对象只是实现了接口,还可以继续继承其他类和实现其他接口。
3. 实现解耦操作,线程任务对象代码可以被多个线程共享,代码和线程独立。
4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。适合做线程池。
*/
public class ThreadDemo {
public static void main(String[] args) {
// b.创建一个线程任务对象
Runnable target = new MyRunnable();
// c.把线程任务对象包装成线程对象
Thread t = new Thread(target);
// d.调用线程对象的start()方法启动线程。
t.start();
Thread t1 = new Thread(target);
t1.start();
for(int i = 0 ; i < 10; i++){
System.out.println(Thread.currentThread().getName()+"=>"+i);
}
}
}
// 线程任务类
// a.定义一个线程任务类实现Runnable接口。
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 10; i++){
System.out.println(Thread.currentThread().getName()+"=>"+i);
}
}
}
方式三:定义一个线程任务类实现Callable接口
/**
a.定义一个线程任务类实现Callable接口。
b.重写call()方法。
c.把线程任务对象包装成一个未来任务对象。
d.把未来任务对象包装成一个线程对象。
e.调用线程对象的start()方法启动线程。
优缺点:
缺点:编码复杂。
优点:全是优点。
可以继续继承其他类。
可以做线程池。
可以得到线程返回的结果。
可以做资源共享操作
*/
public class ThreadDemo {
public static void main(String[] args) {
MyCallable call = new MyCallable();
// c.把线程任务对象包装成一个未来任务对象。
/**
* 未来任务对象: FutureTask
* 1.可以通过未来任务对象去获取线程执行的结果。
* 2.未来任务对象其实就是一个Runnable的对象。
*/
FutureTask<String> target = new FutureTask<>(call);
// d.把未来任务对象包装成一个线程对象。
Thread t = new Thread(target);
// e.调用线程对象的start()方法启动线程。
t.start();
for(int i = 0 ; i < 10; i++){
System.out.println(Thread.currentThread().getName()+"=>"+i);
}
try {
// 线程的执行的结果!
String result = target.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// a.定义一个线程任务类实现Callable接口。申明返回值类型
class MyCallable implements Callable<String>{
// b.重写call()方法。
@Override
public String call() throws Exception {
int count = 0 ;
for(int i = 0 ; i < 10; i++){
count+=(i+1) ; // 1-10的和。
System.out.println(Thread.currentThread().getName()+"=>"+i);
}
return Thread.currentThread().getName()+"求和结果:"+count;
}
}