标签:场景 程序 executor static 多线程 计时 override except 相同
多线程进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
怎么理解?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.
并发是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并行是物理上同时发生,指在某一个时间点同时运行多个程序。
指应用能够交替执行不同的任务, 其实并发有点类似于多线程的原理, 多线程并非是如果你开两个线程同时执行多个任务, 执行, 就是在你几乎不可能察觉到的速度不断去切换这两个任务, 已达到"同时执行效果", 其实并不是的, 只是计算机的速度太快, 我们无法察觉到而已. 就类似于你, 吃一口饭喝一口水, 以正常速度来看, 完全能够看的出来, 当你把这个过程以n倍速度执行时..可以想象一下
指应用能够同时执行不同的任务, 例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行
ava命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
然后提供一些类供我们使用。我们就可以实现多线程程序了。
参考Thread类
我们启动线程使用不是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);
}
}
}
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);
}
}
}
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);
}
}
}
实现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);
}
}
}
实现 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;
}
}
同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
在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) {
/*
重复票数:由原子性所导致,原子性:不割再分割 i-- 不是原子性操作
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();
}
}
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();
}
}
使用步骤:
创建线程池对象
创建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();
}
}
开发中
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();
}
}
标签:场景 程序 executor static 多线程 计时 override except 相同
原文地址:https://blog.51cto.com/13852519/2398883