多线程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
多线程进程概述
进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
多进程的意义
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。
什么是线程
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。
多线程有什么意义
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
怎么理解?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.
并发和并行
并发是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并行是物理上同时发生,指在某一个时间点同时运行多个程序。
什么是并发
指应用能够交替执行不同的任务, 其实并发有点类似于多线程的原理, 多线程并非是如果你开两个线程同时执行多个任务, 执行, 就是在你几乎不可能察觉到的速度不断去切换这两个任务, 已达到"同时执行效果", 其实并不是的, 只是计算机的速度太快, 我们无法察觉到而已. 就类似于你, 吃一口饭喝一口水, 以正常速度来看, 完全能够看的出来, 当你把这个过程以n倍速度执行时..可以想象一下
什么是并行
指应用能够同时执行不同的任务, 例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行
多线程
Java程序运行原理
ava命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。
JVM的启动是多线程的吗?
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
如何实现多线程
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
然后提供一些类供我们使用。我们就可以实现多线程程序了。
参考Thread类
多线程程序实现的方式1
我们启动线程使用不是run方法,而应该是start方法.使该线程开始执行;
Java 虚拟机调用该线程的 run 方法。
为什么要重写run方法?
这个类是一个线程类,那么在这个类中我们可不可以写一些其他的方法呢?
我们可以在写其他的方法,那么其他方法中封装的代码都是需要被我们线程执行的吗? 不一定那么也就是run方法中封装应该是必须被线程执行的代码.
run方法中的代码的书写原则: 一般是比较耗时的代码
列:
public static void main(String[] args) {
//在Java中如何开启一个线程,利用 Thread 这个线程类
//线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
MyThread th1 = new MyThread();
th1.setName("刘亦菲");
th1.setPriority(1);
th1.start();//开启线程的方法
MyThread th2 = new MyThread();
th2.setPriority(Thread.MAX_PRIORITY);
th2.setName("林青霞");
th2.start();
//Java中多个线程,执行是随机性的,因为我Java采用的线程调度模型是抢占式调度,线程优先级高的优先使用CPU的执行权
//优先级一样,就是随机抢占
}
public class MyThread extends Thread {br/>@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
//System.out.println(this.getName()+"=="+i);
System.out.println(Thread.currentThread().getName()+"===" + i);
}
}
}
线程的执行
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
线程只有得到 CPU时间片,也就是使用权,才可以执行指令。
线程调度的两种模式
分时调度模型
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 Java使用的是抢占式调度模型。
设置线程的优先级
public final int getPriority() //获取线程的优先级
public final void setPriority(int newPriority)//设置线程的优先级
线程休眠
线程休眠: public static void sleep(long millis) 线程休眠
列:
public static void main(String[] args) throws InterruptedException {
System.out.println("这是一段广告");
Thread.sleep(5000);
System.out.println("bbbbb");
//Thread(String name) 分配新的 Thread 对象。
MyThread th1 = new MyThread("林青霞");
th1.start();
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(2000);//让线程休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(this.getName()+"=="+i);
System.out.println(Thread.currentThread().getName() + "===" + i);
}
}
}
加入线程
加入线程: public final void join()
意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行
注意事项: 在线程启动之后,在调用方法
列:
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
MyThread th3 = new MyThread("张飞");
//join()方法可以将多个线程并发的执行,转换成串行
//A:
//加入线程:
//public final void join ()
//意思就是:
//等待该线程执行完毕了以后, 其他线程才能再次执行
//注意事项:
//在线程启动之后, 在调用方法
th1.start();
th1.join();
th2.start();
th2.join();
th3.start();
}
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===" + i);
}
}
}
礼让线程
礼让线程: public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。
列:
public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
th1.start();
th2.start();
}
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName() + "===" + i);
}
}
}
守护线程
守护线程: public final void setDaemon(boolean on):
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
列:
public class MyTest {
public static void main(String[] args) {
Thread.currentThread().setName("刘备");
System.out.println("主线程开始了");
System.out.println("主线程开始了");
System.out.println("主线程开始了");
System.out.println("主线程开始了");
//A:
//守护线程:
//public final void setDaemon ( boolean on):
//将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
//该方法必须在启动线程前调用。
//当用户线程,执行完之后,那么守护线程,必须里面死亡
MyThread th1 = new MyThread("张飞");
MyThread th2 = new MyThread("关羽");
//设置为守护线程 该方法必须在启动线程前调用。
th1.setDaemon(true);
th2.setDaemon(true);
th1.start();
th2.start();
System.out.println("主线程结束了");
System.out.println("主线程结束了");
System.out.println("主线程结束了");
System.out.println("主线程结束了");
System.out.println("主线程结束了");
}
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===" + i);
}
}
}
java用户线程和守护线程
- 1.用户线程和守护线程的区别
用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。很好理解,守护线程是用来辅助用户线程的,如公司的保安和员工,各司其职,当员工都离开后,保安自然下班了。
-
2.用户线程和守护线程的适用场景
由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,适用于辅助用户线程的场景,如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等。 -
3.创建守护线程
调用线程对象的方法setDaemon(true),设置线程为守护线程。
1)thread.setDaemon(true)必须在thread.start()之前设置。
2)在Daemon线程中产生的新线程也是Daemon的。
3)不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。
因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。 -
4.Java守护线程和Linux守护进程
两者不是一个概念。Linux守护进程是后台服务进程,没有控制台。
在Windows中,你可以运行javaw来达到释放控制台的目的,在Unix下你加&在命令的最后就行了。所以守护进程并非一定需要的。线程中断
中断线程
public final void stop(): 停止线程的运行
public void interrupt(): 中断线程(这个翻译不太好),查看API可得当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞列:
public class MyTest {
public static void main(String[] args) throws InterruptedException {MyThread th1 = new MyThread("张飞"); th1.start(); Thread.sleep(20); th1.stop();//强制停掉子线程
}
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100000; i++) {System.out.println(Thread.currentThread().getName() + "===" + i); }
}
}列2:
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("张飞");
th1.start();
Thread.sleep(1000);
th1.interrupt();//打断线程的阻塞状态,让线程继续运行
}
}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
try {
Thread.sleep(5000); //让线程休眠,其实是让线程处于了一种阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "===" + i); }
}
}多线程程序实现的方式2
实现Runnable接口 这种方式扩展性强 实现一个接口 还可以再去继承其他类
优点:可以避免由于Java单继承带来的局限性。列:
public class MyTest {
public static void main(String[] args) {
//1. 创建线程的另一种方法是声明实现 Runnable 接口的类。
//2. 该类然后实现 run 方法。然后可以分配该类的实例,
// 3. 在创建 Thread 时作为一个参数来传递并启动
MyRunable myRunable = new MyRunable();//Thread(Runnable target) //分配新的 Thread 对象。 //Thread(Runnable target, String name) //分配新的 Thread 对象。 Thread th = new Thread(myRunable,"王菲"); th.start(); Thread th2 = new Thread(myRunable,"谢霆锋"); th2.start();
}
}
public class MyRunable implements Runnable { //Runable 任务br/>@Override
public void run() {
for (int i = 0; i < 100; i++) {
// System.out.println(this.getName+"==+i");
System.out.println(Thread.currentThread().getName() + "==" + i);
}
}
}多线程程序的实现方式3
实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类列:
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建一个线程的方式3
//A:
//实现 Callable 接口。相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
//
//B:
//执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。FutureTask 是 Future 接口的实现类
//
//C:
//实现步骤
//1. 创建一个类实现Callable 接口
//2. 创建一个FutureTask类将Callable接口的子类对象作为参数传进去
//3. 创建Thread类, 将FutureTask对象作为参数传进去
//4. 开启线程//Callable 返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 // //Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。 // 但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。 MyCallable myCallable = new MyCallable(); FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable); Thread th = new Thread(integerFutureTask); th.start(); //获取线程执行完之后,返回的结果 Integer integer = integerFutureTask.get(); System.out.println(integer);
}
}
public class MyCallable implements Callable<Integer> {return 100;
}
}
线程安全问题产生的原因
- 是否是多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
解决方法:
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
需要使用同步代码块:
synchronized(对象){//不能在括号了直接new 对象 new 了 就没效果 要被同步的代码 ;}
这个同步代码块保证数据的安全性的一个主要因素就是这个对象
注意这个对象 要定义为静态成员变量 才能被所有线程共享 - 需要这个对象被所有的线程对象所共享
- 这个对象其实就是一把锁.
- 这个对象习惯叫做监视器
同步代码块的方式解决线程安全问题及解释以及同步的特点及好处和弊端
- 同步的好处: 同步的出现解决了多线程的安全问题。
-
同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,
因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识。
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。
线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。
获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,
当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,
直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,
但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,
类锁是用于类的静态方法或者一个类的class对象上的。
我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,
所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,
它只是用来帮助我们理解锁定实例方法和静态方法的区别的.列:
public class CellRunable implements Runnable {
static int piao = 100;
static Object obj = new Object();
int i = 0;// th2br/>@Override
public void run() {while (true) { if (i % 2 == 0) { synchronized (CellRunable.class) { //obj 就相当于一把锁 哪个线程一进入同步代码块,就会持有锁,那么这个把所不释放,其他线程就阻塞状态 if (piao > 0) { //模拟延迟 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } //i-- i++ 先使用 后运算 //th3 //th2 100 System.out.println(Thread.currentThread().getName() + "正在售卖 " + (piao--) + " 张票"); } } //哪个线程出了同步代码块就会释放锁 } else { maiPiao(); } i++; }
}
//将 synchronized 加到方法上,我们叫做同步方法
//同步方法用的是this 这个多对象
//静态同步方法使用的是当前类的字节码文件对象
public static synchronized void maiPiao() {
//System.out.println(this);
// 相当于一把锁 哪个线程一进入同步代码块,就会持有锁,那么这个把所不释放,其他线程就阻塞状态
if (piao > 0) {
//模拟延迟try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } //i-- i++ 先使用 后运算 //th3 //th2 100 System.out.println(Thread.currentThread().getName() + "正在售卖 " + (piao--) + " 张票"); }
}
}
public class MyTest {
public static void main(String[] args) {
/*- 通过观察发现,我们发现了一些不合理数据,也就是说在多线程环境下,出现了线程安全问题
- 出现线程安全的问题:得符号三个条件
- 1.是否是多线程环境
- 2.是否有共享数据
- 3.是否有多条语句在操作这个共享数据
-
重复票数:由原子性所导致,原子性:不割再分割 i-- 不是原子性操作
- 出现0票负票:由线程的随机性所导致的
- 怎么解决可以使用同步代码块来解决
-
synchronized (锁对象){
//需要被同步的代码} //锁对象:可以传任意的所对象
-
*/
CellRunable cellRunable = new CellRunable();
Thread th1 = new Thread(cellRunable);
Thread th2 = new Thread(cellRunable);
Thread th3 = new Thread(cellRunable);
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
th1.start();
th2.start();
th3.start();
}
}
JDK5之后的Lock锁的概述和使用
- Lock锁的概述
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock -
Lock和ReentrantLock
void lock()
void unlock()
public class CellRunable implements Runnable {
static int piao = 100;
static Object obj = new Object();
static Lock lock = new ReentrantLock();列:
@Override
public void run() {while (true) { lock.lock(); //加锁 if (piao > 0) { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在售卖 " + (piao--) + " 张票"); } lock.unlock(); //释放锁 }
}
}
public class MyTest {
public static void main(String[] args) {
/ A:
Lock锁的概述
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
B:
Lock和ReentrantLock
void lock ()
void unlock ()/
CellRunable cellRunable = new CellRunable();
Thread th1 = new Thread(cellRunable);
Thread th2 = new Thread(cellRunable);
Thread th3 = new Thread(cellRunable);
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
th1.start();
th2.start();
th3.start();
}
}
死锁
-
死锁问题概述
如果出现了同步嵌套,就容易产生死锁问题
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例
死锁: 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态
举例: 中国人和美国人一起吃饭
中国人使用的筷子
美国人使用的刀和叉
中国人获取到了美国人的刀
美国人获取到了中国人的一根筷子列:
public interface LockeInterface {
//定义两把锁
public static final Object objA=new Object();
public static final Object objB=new Object();
}
public class MyThread extends Thread {
boolean b;public MyThread(boolean b) {
this.b = b;
}@Override
public void run() {
if (b) {
synchronized (LockeInterface.objA) {
System.out.println("true objA 进来了");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LockeInterface.objB) {
System.out.println("true objB 进来了");
}
}
} else {
synchronized (LockeInterface.objB) {
System.out.println("false objB 进来了");
synchronized (LockeInterface.objA) {
System.out.println("false objA 进来了");
}
}//
}
}
}
public class MyTest {
public static void main(String[] args) {
//死锁:
//两个或者两个以上的线程, 在抢占CPU的执行权的时候, 都处于等待状态
MyThread th1 = new MyThread(true);
MyThread th2 = new MyThread(false);
th1.start();
th2.start();
}
}wait()和sleep()的区别
- wait()方法和sleep()方法 都可以使线程处于阻塞状态
- wait() 可以设置时间量,也可以不设置。
- sleep() 是要设置休眠的时间
- sleep() 一旦休眠不会释放锁
- wait() 一旦等待就会释放锁
线程池
- 线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池 - 内置线程池的使用概述
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool(): 根据任务的数量来创建线程对应的线程个数
public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化几个线程
public static ExecutorService newSingleThreadExecutor(): 初始化一个线程的线程池
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task) -
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池列:
public class MyTest {
public static void main(String[] args) {
//线程池:是一个装有一定数量的线程对象的容器,可以帮我们管理这些线程对象,重复利用线程对象,去执行任务
//程序开启一个线程,是比较耗费性能的,因为他涉及到更操作系统进行交互。
//JDK1.5之后,帮我们提供好了线程池,我们可以直接拿来用//JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法 //public static ExecutorService newCachedThreadPool ():根据任务的数量来创建线程对应的线程个数 //public static ExecutorService newFixedThreadPool ( int nThreads):固定初始化几个线程 //public static ExecutorService newSingleThreadExecutor ():初始化一个线程的线程池 //这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者 //ExecutorService 线程池 //获取线程池对象 ExecutorService executorService = Executors.newCachedThreadPool(); //给线程池提交任务即可 executorService.submit(new MyRunable()); executorService.submit(new MyRunable()); executorService.submit(new MyRunable()); executorService.submit(new MyRunable()); executorService.submit(new MyRunable()); //关闭线程池 executorService.shutdown();
//关闭线程池 executorService.shutdown();
}
}
public class MyTest3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//获取一个线程池。里面只有一个线程对象
ExecutorService executorService = Executors.newSingleThreadExecutor();Future<Integer> future = executorService.submit(new MyCallable()); Integer integer = future.get(); System.out.println(integer); executorService.shutdown();
}
}
public class MyCallable implements Callable<Integer> {br/>@Override
public Integer call() throws Exception {
int i=1;return ++i;
}
}匿名内部类的方式实现多线程程序
列:
public class MyTest3 {
public static void main(String[] args) {
//匿名内部类来开启线程
//方式1
new Thread() {br/>@Override
public void run() {
System.out.println("线程执行了");
}
}.start();
//方式2
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行了");
}
}).start();
}
}
定时器
- 定时器概述
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。 - Timer和TimerTask
Timer:
public Timer()
public void schedule(TimerTask task, long delay):
public void schedule(TimerTask task,long delay,long period);
public void schedule(TimerTask task, Date time):
public void schedule(TimerTask task, Date firstTime, long period):
TimerTask
public abstract void run()
public boolean cancel() -
开发中
Quartz是一个完全由java编写的开源调度框架。列:
public class MyTest {
public static void main(String[] args) throws ParseException {
//定时器:Timer// 一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 Timer timer = new Timer(); //可以到时间执行任务 //void schedule (TimerTask task, Date time) //安排在指定的时间执行指定的任务。 //void schedule (TimerTask task, Date firstTime,long period) //安排指定的任务在指定的时间开始进行重复的固定延迟执行。 //void schedule (TimerTask task,long delay) //安排在指定延迟后执行指定的任务。 //void schedule (TimerTask task,long delay, long period) //安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。 //TimerTask由 Timer 安排为一次执行或重复执行的任务。 //boolean cancel () //取消此计时器任务。 MyTimerTask myTimerTask = new MyTimerTask(timer); //timer.schedule(myTimerTask, 3000); //2秒之后执行定时任务 // timer.schedule(myTimerTask,2000,1000);//第一次等2秒执行,后面每隔一秒重复执行。 //myTimerTask.cancel();取消定时任务 //在指定的日期来执行任务 String str = "2019-05-17 15:45:00"; Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str); timer.schedule(myTimerTask, date); // timer.cancel();取消定时器
}
}
public class MyTimerTask extends TimerTask {
Timer timer;
public MyTimerTask(Timer timer) {
this.timer=timer;}
@Override
public void run() {
System.out.println("砰~~~ 爆炸了");
//timer.cancel();
}
}
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章