Thread
实现1:
继承Thread类,重写run方法。
MyThread th = new MyThread();
th.start()
实现2:
实现Runable接口,从写run方法,创建子类对象。传入Thread类的
MyRunable myRunable = new MyRunable();
Thread th = new Thread(myRunable);
th.start();
实现3:
实现 Callable 接口。 (相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常)
- 创建一个类实现Callable 任务接口
- 创建一个FutureTask类将Callable接口的子类对象作为参数传进去
- 创建Thread类, 将FutureTask对象作为参数传进去
- 开启线程*/
FutureTask<Object> task = new FutureTask<>(myCallable);
Thread th = new Thread(task);
th.setName("线程A");
th.start();
//task里的get方法可以获取到返回值
Object o = task.get();
System.out.println(o);
方法:
- public final int getPriority()返回线程的优先级。默认为5
- public final void setPriority(int newPriority)
- public static void sleep(long millis)
- public final void join()等待该线程执行完毕了以后,其他线程才能再次执行
- public static void yield(): (礼让)暂停当前正在执行的线程对象,并执行其他线程
- public final void setDaemon(boolean on)守护线程
- public final void stop()
- public void interrupt()
案例:卖电影票
public class CellThread extends Thread {
//定义一个静态常量被3个线程所共享
static int piao=100;
@Override
public void run() {
while (true) {
if (piao > 0) {
System.out.println(this.getName() + "抢到了第" + (piao--) + "票");
}
}
}
}
public class MyTest {
public static void main(String[] args) {
/* A:
案例演示
需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
通过继承Thread类实现*/
//100张票 属于共享资源,三个窗口共同卖
//三个窗口就相当于三个线程。
CellThread th1 = new CellThread("窗口1");
CellThread th2 = new CellThread("窗口2");
CellThread th3 = new CellThread("窗口3");
th1.start();
th2.start();
th3.start();
}
}
线程安全问题------同步与锁
sychronized:
- 语法:
sychronized(对象){
同步代码;
}
- 一个对象一把锁。互斥锁。将这个对象定义为static。
- 同步方法:方法前加sychronized关键字,锁对象是this
Lock:
子类:ReentrantLock();
方法:lock()+unlock()
典型使用:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
死锁:相互等待
public interface LockUtils {
//定义锁对象A
public static final Object objA = new Object();
//定义锁对象B
public static final Object objB = new Object();
}
public class MyRunnable implements Runnable {
private boolean flag;
public MyRunnable(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockUtils.objA) {
System.out.println("true线程持有A锁,进来了");
synchronized (LockUtils.objB) {
System.out.println("true线程持有B锁,进来了");
}
}
}
else{
synchronized (LockUtils.objB){
System.out.println("false线程持有B锁,进来了");
synchronized (LockUtils.objA){
System.out.println("false线程持有A锁,进来了");
}
}
}
}
}
public class MyTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable(true);
Thread th1 = new Thread(myRunnable);
MyRunnable myRunnable1 = new MyRunnable(false);
Thread th2 = new Thread(myRunnable1);
th1.start();
th2.start();
}
}
等待唤醒机制:
-
void wait () 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
-
void wait (long timeout) 在其他线程调用此对象的 notify () 方法或 notifyAll ()方法,或者超过指定的时间量前,导致当前线程等待。
-
void notify () 唤醒在此对象监视器上等待的单个线程。
-
void notifyAll () 唤醒在此对象监视器上等待的所有线程。
案例:
//生产线程
public class SetThread extends Thread {
private Food food;
public SetThread(Food food) {
this.food = food;
}
int i=0;
@Override
public void run() {
while(true){
//将共享资源的对象设置为锁对象
synchronized (food){
if(Food.flag){
//作为生产线程有资源就进行等待
try {
food.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
food.name = "包子";
food.price = 2;
} else {
food.name = "菜加馍";
food.price = 4;
}
//进行修改标记
Food.flag=true;
//唤醒消费线程,让消费线程进行消费
food.notify();
}
i++;
}
}
}
//消费线程
public class GetThread extends Thread{
private Food food;
public GetThread(Food food) {
this.food = food;
}
@Override
public void run() {
while(true){
synchronized (food){
if(!Food.flag){
//消费线程没有资源就进行等待
//在调用wait()方法之前,线程必须获得该对象的对象级别锁
//即只能在同步代码块或者同步方法中进行调用
//wait()方法使得当前线程状态处于阻塞状态,直到被另外一个线程调用notify()方法进行唤醒。
try {
food.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费资源
System.out.println("食物的品种为:"+food.name+"===="+"食物的价格为:"+food.price+"元");
//当没有资源时(即将资源标记置为false)
Food.flag=false;
//通知生产线程进行生产
//notify()方法需要在同步方法与同步代码块中进行调用
//即在调用前,线程也必须得到该对象的对象级别锁
food.notify();
}
}
}
}
//共享资源(这里将Food设置为共享资源)
public class Food {
public String name;
public int price;
//定义一个标记
//注意false表示没有资源,true表示有资源
public static boolean flag;
}
//测试类
public class MyTest {
public static void main(String[] args) {
Food food = new Food();
SetThread setThread = new SetThread(food);
GetThread getThread = new GetThread(food);
setThread.start();
getThread.start();
}
}
sleep与wait():
- wait()方法一旦等待就会释放锁;slee()方法使线程处于休眠状态,线程休眠后不会释放锁
- wait()方法可以设置等待的时间的量,也可以不设置等待的时间量;sleep()方法必须设置等待的时间量
volatile
内存可见性:
- 当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
- 对于多线程,不是一种互斥关系
- 不能保证变量状态的“原子性操作”
线程状态:
- 新建:线程被创建出来
- 就绪:具有CPU的执行资格,但是不具有CPU的执行权
- 运行:具有CPU的执行资格,也具有CPU的执行权
- 阻塞:不具有CPU的执行资格,也不具有CPU的执行权
- 死亡:不具有CPU的执行资格,也不具有CPU的执行权
CAS算法:
CAS中有3个操作数,需要读写的内存值V,需要比较的值A,以及拟写入的新值B。当且仅当预期值A与内存值V相同时,将内存值 V修改为B,否则什么都不做,保障了原子性。
java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
- AtomicBoolean 、 AtomicInteger 、 AtomicLong 、AtomicReference
- AtomicIntegerArray 、 AtomicLongArray
- AtomicMarkableReference
- AtomicReferenceArray
例:
//在java.util.concurrent.atmoic包下提供了一些原子变量。
// 所谓原子变量,就是类的小工具包,支持在单个变量上解除锁的线程安全编程。
import java.util.concurrent.atomic.AtomicInteger;
public class MyRunnable implements Runnable {
AtomicInteger i= new AtomicInteger(1);
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i.getAndIncrement());//i++
}
}
}
public class MyTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
for (int i = 0; i < 10; i++) {
new Thread(myRunnable).start();
}
}
}
线程池–Executors:
创建大量生存期很短的线程时。
- public static ExecutorService newCachedThreadPool(): 根据任务的数量来创建线程对应的线程个数
- public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化几个线程
- public static ExecutorService newSingleThreadExecutor(): 初始化一个线程的线程池
方法:
- Future<?> submit(Runnable task)
- Future submit(Callable task)
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//给线程池中提交任务
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
//关闭线程池
executorService.shutdown();
}
阿里手册:
public static void main(String[] args) {
//阿里的规范手册中明确:
//线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
// 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
// 说明:Executors 返回的线程池对象的弊端如下:
// 1)FixedThreadPool 和 SingleThreadPool :
// 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
// 2)CachedThreadPool 和 ScheduledThreadPool :
// 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
// ExecutorService executorService = Executors.newCachedThreadPool();
/*
//corePoolSize表示常驻核心线程数,如果大于0,即使本地任务执行完也不会被销毁
//
public ThreadPoolExecutor( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)}*/
}
public static void main(String[] args) {
/* public ThreadPoolExecutor(
int corePoolSize, 表示常驻核心线程数,如果大于0,即使本地任务执行完也不会被销毁
int maximumPoolSize, 表示线程池能够容纳可同时执行的最大线程数
long keepAliveTime, 表示线程池中线程空闲的时间,当空闲时间达到该值时,线程会被销毁,只剩下 `corePoolSize` 个线程位置
TimeUnit unit, `keepAliveTime` 的时间单位,最终都会转换成【纳秒】,因为CPU的执行速度杠杠滴
BlockingQueue<Runnable> workQueue, 当请求的线程数大于 `maximumPoolSize` 时,线程进入该阻塞队列
ThreadFactory threadFactory, 顾名思义,线程工厂,用来生产一组相同任务的线程,同时也可以通过它增加前缀名,虚拟机栈分析时更清晰
RejectedExecutionHandler handler 执行拒绝策略,当 workQueue 达到上限,同时也达到 maximumPoolSize 就要通过这个来处理,比如拒绝,丢弃等,这是一种限流的保护措施
)}
*/
//自己指定创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//提交任务
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("任务执行完了");
}
});
threadPoolExecutor.shutdown();
}
定时器:
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()
public static void main(String[] args) {
//定时器,可以在指定的时间,执行定时任务
//一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
Timer timer = new Timer();
// 类 TimerTask 由 Timer 安排为一次执行或重复执行的任务。
//等待多少毫秒之后,来执行这个定时任务
MyTimerTask myTimerTask = new MyTimerTask(timer);
timer.schedule(myTimerTask, 1000 * 5);
//取消定时任务
//myTimerTask.cancel();
//取消定时器:终止此计时器,丢弃所有当前已安排的任务。
//timer.cancel();
}
}
class MyTimerTask extends TimerTask {
private Timer timer;
public MyTimerTask(Timer timer) {
this.timer = timer;
}
@Override
public void run() {
System.out.println("砰~~~爆炸了");
//取消定时器
//timer.cancel();
}
public static void main(String[] args) {
Timer timer = new Timer();
//第一次等待两秒执行任务,以后间隔1s不间断的执行异步任务。
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("砰~~爆炸了");
}
}, 1000 * 2, 1000);
}
开发中:
Quartz是一个完全由java编写的开源调度框架。