一.线程安全
1.线程安全的引入
真实售票案例中,会出现一定的网络延迟.
此时会出现两个问题
1.出现0张票或负数票:原因,是由于线程的随机性所导致的。
当只剩下一张票时,此时a线程抢到了CPU执行权,判断piao>=1后进行休眠状态,piao仍然为1,此时b线程抢到了CPU执行权,if判断为true,进行休眠状态,此时c线程抢到了CPU执行权,然后进行休眠状态,然后等a,b,c线程休眠完毕后,卖出的票为第1张,第0张,第-1张票。
2.出现相同的票:原因,就是由于线程的原子性所导致的 原子性(不可分割性)
线程对 (piao-- 不是一个原子性操作他要对paio这个变量要进行 读 改 写 三个操作 )
java内存模型:
Java内存分为主存和工作内存,piao变量存放在主存中,需要时读取到每个线程各自的工作内存。
当a线程读取到100,改为99,没有写回到主存时,b线程就从主存中读取了100,所以就出现了相同票的情况,即线程的原子性导致的.
package org. westos. demo;
public class CellRunnable implements Runnable {
static int piao= 100 ;
static Object object= new Object ( ) ;
public CellRunnable ( ) {
}
@Override
public void run ( ) {
while ( true ) {
synchronized ( object) {
if ( piao >= 1 ) {
休眠, b线程进来休眠, 此时c线程抢到了CPU执行权,然后进行休眠状态,然后等a,b,c线程休眠完毕后,卖出的票为第1 张,第0 张,第- 1 张票。
try {
Thread. sleep ( 2 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
}
}
}
-- -- -- -- -- -- -- -- --
package org. westos. demo;
public class MyTest {
public static void main ( String[ ] args) {
CellRunnable cellRunnable = new CellRunnable ( ) ;
Thread th1 = new Thread ( cellRunnable, "1窗口" ) ;
Thread th2 = new Thread ( cellRunnable, "2窗口" ) ;
Thread th3 = new Thread ( cellRunnable, "3窗口" ) ;
th1. start ( ) ;
th2. start ( ) ;
th3. start ( ) ;
}
}
2.线程安全问题的原因
A:首先想为什么出现问题?(也是我们判断是否有问题的标准)
是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据
B:如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境。
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
- 判断一个多线程应用程序是否有问题的标准:
- a: 是否是多线程环境
- b: 是否存在共享数据
- c: 是否存在多条语句同时操作共享数据
-
- 我们现在这个程序是存在问题的,因为它满足上面的标准,那么我们只要将这个标准打乱,那么我们就可以解决这个问题.
- 而上面的标准中a , b是不能打乱的,因此我们只能对c做处理,关键是怎么处理? 如果我们把操作共享数据的多条语句看做
- 成一个整体,当一个线程执行这个整体的时候,其他的线程处于等待状态,也就说当一个线程执行这个整体的时候,其他线程
- 不能进行执行,那么怎么做到这个一点呢?
-
- 需要使用同步代码块:
-
- 格式:
-
- synchronized(对象){//不能在括号了直接new 对象 new 了 就没效果
- 要被同步的代码 ;
- }
*
- 这个同步代码块保证数据的安全性的一个主要因素就是这个对象
注意这个对象 要定义为静态成员变量 才能被所有线程共享
- 需要这个对象被所有的线程对象所共享
- 这个对象其实就是一把锁.
- 这个对象习惯叫做监视器
同步方法:
同步方法,将synchronized关键字写在方法上
同步方法:默认用的锁对象是this,谁调用方法,谁就代表this
package org. westos. demo3;
public class CellRunnable implements Runnable {
static int piao = 100 ;
static Object object = new Object ( ) ;
int i = 0 ;
public CellRunnable ( ) {
}
@Override
public void run ( ) {
while ( true ) {
if ( i % 2 == 0 ) {
synchronized ( object) {
if ( piao >= 1 ) {
try {
Thread. sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
} else {
soldTecket ( ) ;
}
i++ ;
}
}
public synchronized void soldTecket ( ) {
System. out. println ( "卖票" ) ;
while ( true ) {
if ( piao >= 1 ) {
try {
Thread. sleep ( 2 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
}
}
-- -- -- -- -- -- -- -- -- -- -- -
package org. westos. demo3;
public class MyTest {
public static void main ( String[ ] args) {
CellRunnable cellRunnable = new CellRunnable ( ) ;
Thread th1 = new Thread ( cellRunnable, "1" ) ;
Thread th2 = new Thread ( cellRunnable, "2" ) ;
Thread th3 = new Thread ( cellRunnable, "3" ) ;
th1. start ( ) ;
th2. start ( ) ;
th3. start ( ) ;
}
}
3.同步代码块与同步方法
package org. westos. demo3;
public class CellRunnable implements Runnable {
static int piao = 100 ;
static Object object = new Object ( ) ;
int i = 0 ;
public CellRunnable ( ) {
}
@Override
public void run ( ) {
while ( true ) {
if ( i % 2 == 0 ) {
synchronized ( this ) {
if ( piao >= 1 ) {
try {
Thread. sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
} else {
soldTecket ( ) ;
}
i++ ;
}
}
public synchronized void soldTecket ( ) {
System. out. println ( "卖票" ) ;
if ( piao >= 1 ) {
try {
Thread. sleep ( 2 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
package org. westos. demo3;
public class MyTest {
public static void main ( String[ ] args) {
CellRunnable cellRunnable = new CellRunnable ( ) ;
Thread th1 = new Thread ( cellRunnable, "1" ) ;
Thread th2 = new Thread ( cellRunnable, "2" ) ;
Thread th3 = new Thread ( cellRunnable, "3" ) ;
th1. start ( ) ;
th2. start ( ) ;
th3. start ( ) ;
}
}
4.静态同步方法
同步方法:我们可以把一个方法用synchronized这个关键字修饰,来封装一段代码,来解决线程安全问题
静态同步方法:默认用的锁对对象,用的是当前类的字节码对象
package org. westos. demo4;
import org. westos. demo4. CellRunnable;
public class MyTest {
public static void main ( String[ ] args) {
CellRunnable cellRunnable = new CellRunnable ( ) ;
Thread th1 = new Thread ( cellRunnable, "1" ) ;
Thread th2 = new Thread ( cellRunnable, "2" ) ;
Thread th3 = new Thread ( cellRunnable, "3" ) ;
th1. start ( ) ;
th2. start ( ) ;
th3. start ( ) ;
}
}
class CellRunnable implements Runnable {
static int piao= 100 ;
static int i= 0 ;
@Override
public void run ( ) {
while ( true ) {
if ( i % 2 == 0 ) {
synchronized ( CellRunnable. class ) {
if ( piao >= 1 ) {
try {
Thread. sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
} else {
soldTecket ( ) ;
}
i++ ;
}
}
public static synchronized void soldTecket ( ) {
if ( piao >= 1 ) {
try {
Thread. sleep ( 2 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
}
}
5.同步的好处与弊端
A:同步代码块的格式
格式:
synchronized(对象){ //同步代码代码块上的锁,是一个互斥锁。
死循环
需要同步的代码;
}
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能
B:同步的好处: 同步的出现解决了多线程的安全问题。
C:同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,
因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识。
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。
线程进入同步代码块或同步方法的时候会自动获得该锁,在退出同步代码块或同步方法时会释放该锁。
获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,
当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,
直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,
但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,
类锁是用于类的静态方法或者一个类的class对象上的。
我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,
所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,
它只是用来帮助我们理解锁定实例方法和静态方法的区别的.
二.Lock锁
1.概述
A:Lock锁的概述
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
2.使用
Lock和ReentrantLock
void lock() 加锁
void unlock() 释放锁
//Collections中的synchronizedList()方法可以把ArrayList转换成一个线程安全的集合
List list = Collections.synchronizedList(new ArrayList<String>());
Lock锁的使用
建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
//释放锁 不管有没有遇到异常,锁必须释放
lock.unlock()
}
}
}
package org. westos. demo5;
import java. util. concurrent. locks. Lock;
import java. util. concurrent. locks. ReentrantLock;
public class MyTest {
public static void main ( String[ ] args) {
MyRunnable cellRunnable = new MyRunnable ( ) ;
Thread th1 = new Thread ( cellRunnable, "1" ) ;
Thread th2 = new Thread ( cellRunnable, "2" ) ;
Thread th3 = new Thread ( cellRunnable, "3" ) ;
th1. start ( ) ;
th2. start ( ) ;
th3. start ( ) ;
}
}
class MyRunnable implements Runnable {
static int piao= 100 ;
static ReentrantLock lock= new ReentrantLock ( ) ;
@Override
public void run ( ) {
while ( true ) {
try {
lock. lock ( ) ;
if ( piao >= 1 ) {
try {
Thread. sleep ( 20 ) ;
} catch ( Exception e1) {
e1. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "正在卖第" + ( piao-- ) + "票" ) ;
}
} catch ( Exception e) {
e. printStackTrace ( ) ;
} finally {
lock. unlock ( ) ;
}
}
}
}
三.死锁
1.概述
A:死锁问题概述
如果出现了同步嵌套,就容易产生死锁问题
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例
死锁: 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态
举例: 中国人和美国人一起吃饭
中国人使用的筷子
美国人使用的刀和叉
中国人获取到了美国人的刀
美国人获取到了中国人的一根筷子
package org. westos. demo6;
import static org. westos. demo6. Utils. A;
public class MyTest {
public static void main ( String[ ] args) {
EatThread th1 = new EatThread ( false ) ;
EatThread th2 = new EatThread ( true ) ;
th1. start ( ) ;
th2. start ( ) ;
}
}
class EatThread extends Thread {
boolean flag= false ;
public EatThread ( boolean flag) {
this . flag = flag;
}
@Override
public void run ( ) {
if ( flag) {
synchronized ( Utils. A) {
System. out. println ( "true线程拥有A锁" ) ;
synchronized ( Utils. B) {
System. out. println ( "true线程拥有B锁" ) ;
}
}
} else {
synchronized ( Utils. B) {
System. out. println ( "false线程拥有B锁" ) ;
synchronized ( Utils. A) {
System. out. println ( "false线程拥有A锁" ) ;
}
}
}
}
}
interface Utils {
public static final Object A= new Object ( ) ;
public static final Object B= new Object ( ) ;
}
打印结果:
false 线程拥有B锁
true 线程拥有A锁
可以在1 线程开启后休眠一段时间线程, 这样1 线程就会立马持有两把锁, 立即执行完. 第二个线程延迟开启, false 线程后开
2.线程间的等待唤醒机制
Object 类中
void wait () 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
void wait (long timeout) 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
void notify () 唤醒在此对象监视器上等待的单个线程。
void notifyAll () 唤醒在此对象监视器上等待的所有线程。
生产者与消费者:
前面所学的线程都是同一种类的线程,现在学习两种不同种类的线程,一个是生产者线程,另一个是消费者线程。
这就会涉及不同种类的线程之间通信问题
package org. westos. demo;
public class Student {
public String name;
public int age;
public boolean flag= false ;
public Student ( ) {
}
}
-- -- -- -- -- -- -- -- -- -- --
package org. westos. demo;
public class SetThread extends Thread {
Student student;
int i= 1 ;
public SetThread ( Student student) {
this . student= student;
}
@Override
public void run ( ) {
while ( true ) {
synchronized ( student) {
if ( student. flag) {
try {
student. wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
if ( i % 2 == 0 ) {
student. name = "李白" ;
student. age = 22 ;
} else {
student. name = "杜甫" ;
student. age = 33 ;
}
student. flag= true ;
student. notify ( ) ;
}
i++ ;
}
}
}
-- -- -- -- -- -- -- -- -- -- -
package org. westos. demo;
public class GetThread extends Thread {
Student student;
public GetThread ( Student student) {
this . student= student;
}
@Override
public void run ( ) {
while ( true ) {
synchronized ( student) {
if ( ! student. flag) {
try {
student. wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
System. out. println ( student. name + "===" + student. age) ;
student. flag= false ;
student. notify ( ) ;
}
}
}
}
-- -- -- -- -- -- -- -- -
package org. westos. demo;
public class MyTest {
public static void main ( String[ ] args) {
Student student = new Student ( ) ;
SetThread th1 = new SetThread ( student) ;
GetThread th2 = new GetThread ( student) ;
th1. start ( ) ;
th2. start ( ) ;
}
}
3.wait()方法和sleep()方法的区别
wait() 方法和 sleep() 方法的区别
共同点:都可以使线程处于阻塞状态
sleep() 必须设置时间量
wait() 方法可以设置时间量,也可以不设置时间量
wait() 一旦等待,就会释放锁。
sleep() 一旦休眠,不释放锁。
四.内存可见性问题
1.volatile 关键字
volatile 解决内存可见性问题
一、Java内存模型
想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。
Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,
线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。
线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。
不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
3.Java中的可见性
对于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,
当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,
并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
相较于 synchronized 是一种较为轻量级的同步策略。
volatile 变量,用来确保将变量的更新操作通知到其他线程。
可以将 volatile 看做一个轻量级的锁,但是又与
锁有些不同:
对于多线程,不是一种互斥关系
不能保证变量状态的“原子性操作”
package org. westos. demo2;
public class MyTest {
public static void main ( String[ ] args) {
MyRunnable myRunnable = new MyRunnable ( ) ;
Thread th1 = new Thread ( myRunnable, "th1" ) ;
th1. start ( ) ;
while ( true ) {
if ( myRunnable. getFlag ( ) ) {
System. out. println ( "flag为true,进来执行了" ) ;
break ;
}
}
}
}
class MyRunnable implements Runnable {
volatile boolean flag= false ;
public boolean isFlag ( ) {
return flag;
}
public void setFlag ( boolean flag) {
this . flag = flag;
}
public boolean getFlag ( ) {
return flag;
}
public MyRunnable ( ) {
}
public MyRunnable ( boolean flag) {
this . flag = flag;
}
@Override
public void run ( ) {
try {
Thread. sleep ( 20 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
flag= true ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + "线程执行了,此时flag为" + flag) ;
}
}
2.CAS算法
CAS 算法:看有没有人先一步将内存值进行更新,如果有放弃此次操作,没有就更新
保证更新时只能有一个线程对内存值进行操作
CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器
操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并
发访问。
CAS 是一种无锁的非阻塞算法的实现。
CAS 包含了 3 个操作数:
需要读写的内存值 V
进行比较的值 A
拟写入的新值 B
当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的
值,否则不会执行任何操作。
jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。
java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
AtomicBoolean 、 AtomicInteger 、 AtomicLong 、 AtomicReference
AtomicIntegerArray 、 AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
注:t1,t2线程是同时更新同一变量56的值
因为t1和t2线程都同时去访问同一变量56,所以他们会把主内存的值完全拷贝一份到自己的工作内存空间,所以t1和t2线程的预期值都为56。
假设t1在与t2线程竞争中线程t1能去更新变量的值,而其他线程都失败。
(失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次发起尝试)。
t1线程去更新变量值改为57,然后写到内存中。此时对于t2来说,内存值变为了57,
与预期值56不一致,就操作失败了(想改的值不再是原来的值)。
(上图通俗的解释是:CPU去更新一个值,但如果想改的值不再是原来的值,
操作就失败,因为很明显,有其它操作先改变了这个值。)
就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,
然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所。
容易看出 CAS 操作是基于共享数据不会被修改的假设,
采用了类似于数据库的commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。
package org. westos. demo3;
import java. util. concurrent. atomic. AtomicInteger;
public class MyTest {
public static void main ( String[ ] args) {
MyRunnable myRunnable = new MyRunnable ( ) ;
Thread th1 = new Thread ( myRunnable, "th1" ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
new Thread ( myRunnable) . start ( ) ;
}
}
}
class MyRunnable implements Runnable {
AtomicInteger piao= new AtomicInteger ( 1 ) ;
@Override
public void run ( ) {
while ( true ) {
try {
Thread. sleep ( 20 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
int andIncrement = piao. getAndIncrement ( ) ;
System. out. println ( andIncrement) ;
}
}
}
3.线程的状态转换图
4.匿名内部类来开启线程
package org. westos. demo3;
public class MyTest2 {
public static void main ( String[ ] args) {
Thread th1 = new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( "线程执行1" ) ;
}
} ;
th1. start ( ) ;
new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( "线程执行2" ) ;
}
} . start ( ) ;
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( "线程执行3" ) ;
}
} ) . start ( ) ;
}
}
五.线程池
1.概述
A:线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
B:内置线程池的使用概述
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实例
关闭线程池
线程池:容器,存有一定数量线程对象的容器,线程池可以复用管理线程对象。
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool ():根据任务的数量来创建线程对应的线程个数
当线程池的线程刚创建时,让他们进入阻塞状态:等待某个任务的到来。
如果任务来了,那就好办,唤醒其中一个线程,让它拿到任务去执行即可
2.方法:
public static ExecutorService newCachedThreadPool ():创建线程池
根据任务的数量来创建线程对应的线程个数
public static ExecutorService newFixedThreadPool ( int nThreads):
固定初始化几个线程
public static ExecutorService newSingleThreadExecutor ():
初始化一个线程的线程池
package org. westos. demo4;
import javax. sql. rowset. CachedRowSet;
import java. util. concurrent. *;
public class MyTest {
public static void main ( String[ ] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors. newCachedThreadPool ( ) ;
executorService. submit ( new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "线程执行了这个任务" ) ;
}
} ) ;
executorService. submit ( new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "线程执行了这个任务" ) ;
}
} ) ;
Future< Integer> future = executorService. submit ( new MyCallable ( ) ) ;
Integer num = future. get ( ) ;
System. out. println ( num) ;
executorService. shutdown ( ) ;
}
}
class MyCallable implements Callable < Integer> {
@Override
public Integer call ( ) throws Exception {
System. out. println ( "call方法执行" ) ;
return 1 ;
}
}
-- -- -- -- -- -- -- -- -- --
package org. westos. demo4;
import java. util. concurrent. ExecutorService;
import java. util. concurrent. Executors;
public class MyTest2 {
public static void main ( String[ ] args) {
ExecutorService executorService = Executors. newFixedThreadPool ( 3 ) ;
executorService. submit ( new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( "线程执行了" ) ;
}
} ) ;
executorService. shutdown ( ) ;
}
}
-- -- -- -- -- -- -- -
package org. westos. demo5;
import java. util. concurrent. ExecutorService;
import java. util. concurrent. Executors;
public class MyTest {
public static void main ( String[ ] args) {
ExecutorService executorService = Executors. newSingleThreadExecutor ( ) ;
executorService. submit ( new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( "线程执行了任务" ) ;
}
} ) ;
}
}
六.定时器
1.概述
A:定时器概述
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
B: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编写的开源调度框架。
2.指定时间的定时器
package org. westos. demo5;
import java. util. Timer;
import java. util. TimerTask;
public class MyTest2 {
public static void main ( String[ ] args) {
Timer timer = new Timer ( ) ;
MyTimerTask myTimerTask = new MyTimerTask ( timer) ;
timer. schedule ( myTimerTask, 3000 , 1000 ) ;
myTimerTask. cancel ( ) ;
}
}
class MyTimerTask extends TimerTask {
private Timer timer;
public MyTimerTask ( Timer timer) {
this . timer = timer;
}
@Override
public void run ( ) {
System. out. println ( "炸弹爆炸" ) ;
}
}
3.指定日期
package org. westos. demo5;
import java. sql. Time;
import java. text. ParseException;
import java. text. SimpleDateFormat;
import java. util. Date;
import java. util. Timer;
import java. util. TimerTask;
public class MyTest3 {
public static void main ( String[ ] args) throws ParseException {
Timer timer = new Timer ( ) ;
String str= "2020-06-11 17:17:00" ;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ) ;
Date date = simpleDateFormat. parse ( str) ;
MyTask task = new MyTask ( ) ;
timer. schedule ( task, date, 1000 ) ;
}
}
class MyTask extends TimerTask {
@Override
public void run ( ) {
System. out. println ( "炸弹爆炸了" ) ;
}
}
4.指定时间删除文件夹
package org. westos. demo5;
import java. io. File;
import java. text. ParseException;
import java. text. SimpleDateFormat;
import java. util. Date;
import java. util. Timer;
import java. util. TimerTask;
public class MyTest4 {
public static void main ( String[ ] args) throws ParseException {
Timer timer = new Timer ( ) ;
String str= "2020-06-11 17:27:00" ;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ) ;
Date date = simpleDateFormat. parse ( str) ;
MyTimer myTimer = new MyTimer ( ) ;
timer. schedule ( myTimer, date) ;
}
}
class MyTimer extends TimerTask {
File srcFolder= new File ( "C:\\Users\\user\\Desktop\\demo2" ) ;
@Override
public void run ( ) {
deleteFolder ( srcFolder) ;
}
public static void deleteFolder ( File srcFolder) {
File[ ] files = srcFolder. listFiles ( ) ;
for ( File f : files) {
if ( f. isFile ( ) ) {
f. delete ( ) ;
} else {
deleteFolder ( f) ;
}
}
srcFolder. delete ( ) ;
}
}