线程安全
多线程访问共享数据,就会产生线程安全问题
代码实现
package Thread.demo2;
/*idea:模拟电影院买票,三个窗口共卖100张票
* 分析:
* 3个线程为三个窗口进行买票
* 一个Runnable实现类对象产生100张票*/
public class MyThread1 implements Runnable {
//定义100张票
private int ticket=100;
@Override
public void run() {
while(true){
if(ticket>0){
//为了提高出现问题的概率,让线程睡眠一下
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在卖第"+ticket+"张票");
ticket--;
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
//创建Runnable实现类的对象
MyThread1 mt=new MyThread1();
//创建Thread类对象构造方法中传递Runnable实现类的对象
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
//调用Thread类的start方法开启多线程
t1.start();
t2.start();
t3.start();
}
}
运行结果
Thread-0在卖第100张票
Thread-2在卖第100张票
Thread-1在卖第100张票
分析产生原理:
- 线程同步
1.同步代码块
原理
2.同步方法
同步方法的锁对象就是实现类对象也就是this
静态同步方法:(了解)
锁对象是Runnable.class
3.Lock锁
接口java.util.concurrent.locks
Lock l = new ReentrantLock();
l.lock();
try { // access the resource protected by this lock }
finally { l.unlock(); }
线程状态
- 线程之间的通信:
等待唤醒案例分析:
等待唤醒案例实现:
/*idea:模仿用户买包子和老板做包子(5秒)
* 分析:
* 顾客和老板进程放在同步代码synchronized(锁对象)块中
* 两者只能有一个执行,不能同时执行
* 1创建顾客线程-告知老板买包子后-锁对象调用wait方法等待
* 2创建并执行老板线程--sleep(5000)花费5秒做包子--然后告知顾客做好了-锁对象调用notify()唤醒顾客线程
* 3顾客开始吃包子*/
public class demo {
public static void main(String[] args) {
//创建锁对象
Object o = new Object();
//创建顾客线程
new Thread() {
@Override
public void run() {
synchronized (o) {
System.out.println("老板,我要买包子");
try {
o.wait();//被唤醒后执行后边代码
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒后执行的代码
System.out.println("包子来了,开吃!");
}
}
}.start();
//创建老板线程
new Thread() {
@Override
public void run() {
//睡眠5秒做包子
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
System.out.println("包子做好了!");
o.notify();
}
}
}.start();
//
}
}
等待和唤醒方法:
等待唤醒机制
1.1线程间通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同
1.2等待唤醒机制:
- E:\IdeaProjects\Base_Code\src\Thread\demo3\demo1
线程池
底层原理
- 线程池的使用
1.Executors:线程池的工厂类,用来生成线程池
java.util.concurrent.Executors
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
参数:
int nThreads 创建线程池中包含的线程数量
返回值:
ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接受(面向接口编程)
2.ExecutorService接口:线程池接口
java.util.concurrent ExecutorService
ExecutorService接口方法:
用来从线程池中获取线程,调用start方法,执行线程程任务
submit(Runnable task) 提交一个可运行的任务Runnable去执行
关闭销毁线程池的方法(不建议使用):
shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。
- 线程池的使用步骤:
- 使用线程池的工厂类Executors里边的静态方法newFixedThreadPool创建包含一定线程数量的线程池
- 创建一个类实现Runnable接口,重写run方法,设置线程任务
- 调用ExecutorService接口中的submit(Runnable task) 方法,传递线程任务(Runnable实现类),开启线程
- 调用ExecutorService接口中的shutdown方法销毁线程池(不建议执行)
eg:
/*线程池的使用*/
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 执行此任务!");
}
}
public class main {
public static void main(String[] args) {
//1. 使用线程池的工厂类Executors里边的静态方法newFixedThreadPool创建包含一定线程数量的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//2. 创建一个类实现Runnable接口,重写run方法,设置线程任务
MyThread mt=new MyThread();
//3. 调用ExecutorService接口中的submit(Runnable task) 方法,传递线程任务(Runnable实现类),开启线程
pool.submit(mt);
pool.submit(new MyThread());
pool.submit(new MyThread());
//4. 调用ExecutorService接口中的shutdown方法销毁线程池(不建议执行)
pool.shutdown();
//当执行shutdown操作后使用线程池会异常
// pool.submit(new MyThread());//RejectedExecutionException
}