- 从入口main到结束,一条路走到底
- 好处:没有安全隐患
- 缺点:效率低
- 解决缺点:让方法一同运行起来,为程序开启多个执行的路,每个执行的路,成为线程。
- 示例代码:
```
public static void main(String[] args){
add();
remove();
get();
System.out.println(222);
}
public static void add(){
// 一万次循环
}
public static void remove(){
}
public static void get(){
}
```
大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板, dos窗口等软件。此时,这些程序是在同时运行,”感觉这些软件好像在同一时刻运行着“。
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
public class Demo {
public static void main(String[] args) {
fun();
System.out.println(Math.abs(-9));
}
public static void fun() {
for(int i=0;i<10000; i++) {
System.out.println(i);
}
}
}
java.lang
Class Thread
java.lang.Object
java.lang.Thread
All Implemented Interfaces:
Runnable
Direct Known Subclasses:
ForkJoinWorkerThread
线程是程序中执行的线程。 Java虚拟机允许应用程序同时运行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于具有较低优先级的线程执行。 每个线程可能也可能不会被标记为守护进程。
当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。
- 常用构造方法
Thread()
Allocates a new Thread object.
Thread(Runnable target)
Allocates a new Thread object.
Thread(Runnable target, String name)
Allocates a new Thread object.
Thread(String name)
Allocates a new Thread object.
- 常用方法
void run()
If this thread was constructed using a separate Runnable run object, then that Runnable object‘s run method is called; otherwise, this method does nothing and returns.
static void sleep(long millis)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.
void start()
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
public class SubThread extends Thread {
@Override
public void run() {
for(int i=0; i<50; i++) {
System.out.println("run方法中的变量:"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) { // 1、JVM从入口main开始执行主线程,从CPU开启第一条线程路径,执行main()方法。
SubThread subThread = new SubThread(); // 2、创建线程对象,相当于开启了一个新的线程,从CPU开启第二条线程路径,执行run()方法。
subThread.start(); // 3、执行start方法时,调用run()方法,与main()方法同时要调用CPU,两个执行路径都会被CPU执行,CPU自己选择的权利,出现执行结果,随机性结果。
for(int i=0; i<50; i++) {
System.out.println("main方法中的变量:"+i);
}
}
}
Thread t1 = new Thread();t1.start();
Java API 中
String getName() // 返回线程的名字
static Thread currentThread() // 获取当前线程
public class NameThread extends Thread {
@Override
public void run() {
System.out.println(super.getName()); // 输出本线程的名字
}
}
/*
* 每个线程都有自己的名字
* 运行方法main线程的名字是"main"
* 其他线程也有名字,默认为"Thread-0","Thread-1", ...
* 获得主线程的名字的方法:JVM开启主线程,运行方法main,主线程也是线程,是线程必然是Thread类的对象,Thread类中的静态方法:
* static Thread currentThread() 返回正在执行的线程对象
* 该对象调用getName方法,获取线程名字
*/
public class ThreadDemo2 {
public static void main(String[] args) {
NameThread nameThread = new NameThread();
nameThread.start();
// 获取主线程的线程名
System.out.println(Thread.currentThread().getName());
}
}
Java API 中
Thread(String name) // 分配一个新的名字为name的对象
void setName(String name) // 改变线程的名字为name
方法一:
// 在主线程中
NameThread nameThread = new NameThread();
nameThread.setName("Thread线程名字");
// 在创建的线程类中的run方法中,调用Thread父类构造函数
super("Thread线程名字");
public class SleepThread extends Thread{
@Override
public void run() {
for(int i=0; i<5; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
SleepThread sleepThread = new SleepThread();
sleepThread.start();
Thread.sleep(2000);
}
}
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
为何要实现Runnable接口,Runable是啥玩意呢?继续API搜索。
查看Runnable接口说明文档:Runnable接口用来指定每个线程要执行的任务。包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。
// Runnable接口中只有一个run方法
// 方法摘要
void run() // 当使用实现接口Runnable的对象来创建线程时,启动该线程会导致在该单独执行的线程中调用该对象的run方法。
// 创建线程使用Thread类中的构造方法:
Thread(Runnable target) // 分配一个线程对象,参数为Runnable接口实现类的对象
public class SubRunnable implements Runnable{
@Override
public void run() {
for(int i=0; i<50; i++) {
System.out.println("run..."+i);
}
}
}
/**
* 实现接口方式的线程
* 创建Thread类对象,构造方法中,传递Runnable接口实现类对象
* 调用Thread类的方法start
*/
public class RunnableDemo {
public static void main(String[] args) {
SubRunnable subRunnable = new SubRunnable();
Thread t = new Thread(subRunnable);
t.start();
for(int i=0; i<50; i++) {
System.out.println("main..."+i);
}
}
}
线程状态图:
线程池示意图:
- 在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个JVM里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
- 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
ArrayList<Thread> threads = new ArrayList<Thread>();
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
threads.add(new Thread());
// 程序一开始的时候,创建多个线程对象,存储到集合中,需要线程,从集合中获取线程出来
Thread t = threads.remove(0);
// 使用线程
t.start();
// 线程用完,回到容器中继续等待使用
threads.add(t);
通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。
? - Executors:线程池创建工厂类
? - public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
? - ExecutorService:线程池类
? - Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
? - Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
? - 创建线程池对象
? - 创建Runnable接口子类对象
? - 提交Runnable接口子类对象
? - 关闭线程池
示例:
public class ThreadPoolRunnable implements Runnable{
@Override
public void run() {
System.out.println(new Thread().getName());
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
* JDK1.5之后的新特性,实现线程池程序
* 1、使用工厂类,Executors中的静态方法创建线程对象,指定线程个数
* 2、static ExecutorsService newFixedThreadPool(int 线程个数) 返回线程池对象
* 3、返回的是 ExecutorsService接口的实现类(线程池对象)
* 4、接口实现类对象,调用方法submit(Runnable r) 提交线程,执行任务
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
// 调用工厂类的静态方法,创建线程池对象
// Executors.newFixedThreadPool(2);返回线程池对象,是接口的实现类对象
ExecutorService es = Executors.newFixedThreadPool(2);
// 调用接口实现类对象es的方法submit,提交一个线程任务
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
}
}
<T> Future<T> submit(Callable<T> task)
:获取线程池中的某一个线程对象,并执行线程中的call()方法使用线程池中线程对象的步骤:
? - 创建线程池对象
? - 创建Callable接口子类对象
? - 提交Callable接口子类对象
? - 关闭线程池
示例:
import java.util.concurrent.Callable;
public class ThreadPoolCallable implements Callable<String>{
@Override
public String call() throws Exception {
return "abc";
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(2);
// 提交任务的方法,返回Future接口的实现类
Future<String> f = es.submit(new ThreadPoolCallable());
String s = f.get();
System.out.println(s);
}
}
import java.util.concurrent.Callable;
/*
* 多线程的异步计算
*
*/
public class GetSumCallable implements Callable<Integer>{
private int a;
public GetSumCallable(int a) {
this.a = a;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i=0; i<=a; i++) {
sum += i;
}
return sum;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(2);
Future<Integer> f1 = es.submit(new GetSumCallable(100)); // 加法
Future<Integer> f2 = es.submit(new GetSumCallable(200)); // 减法
System.out.println("1+2+...+100 = "+f1.get());
System.out.println("1+2+...+200 = "+f2.get());
es.shutdown();
}
}
原文:https://www.cnblogs.com/luoyu113/p/10236240.html