目录
5.2.1 创建50个线程 Executors.newCachedThreadPool();
5.2.2 单线程 Executors.newSingleThreadExecutor();
5.2.3 定义几个即运行几个 Executors.newFixedThreadPool(n);
1.生命周期(五种状态--创建、就绪、运行、阻塞、死亡)
1.1 创建线程
1.1.1 继承Thread--实例
public class Thread01 extends Thread {
private String name;//给线程名称
public Thread01(String name) {
this.name = name;
}
//线程运行
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.name+"--"+i);//拥有时间片资源--即运行
try {
//线程阻塞
//Thread01继承Thread,则拥有Thread的方法 可以直接调用方法
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//实例化对象 创建
Thread t1 = new Thread01("A");
Thread t2 = new Thread01("B");
Thread t3 = new Thread01("C");
//就绪
t1.start();
t2.start();
t3.start();
//运行结束--线程死亡
}
}
运行结果部分截图如下所示:
1.1.2 实现Runnable接口--实例
public class Thread02 implements Runnable{
private String name;//给线程名称
public Thread02(String name) {
this.name = name;
}
//线程运行
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.name+"--"+i);//拥有时间片资源--即运行
try {
//线程阻塞
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// //实例化对象 创建
// Thread t1 = new Thread(new Thread02("A"));
// Thread t2 = new Thread(new Thread02("B"));
// Thread t3 = new Thread(new Thread02("C"));
// //就绪
// t1.run();
// t2.run();
// t3.run();
// //运行结束--线程死亡
/*lambda表达式*/
// new Thread(()-> {
// for(int i=1;i<=10;i++) {
// System.out.println(Thread.currentThread().getName()+"-"+i);
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }).start();;
/** 匿名内部类 */
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();;
}
}
1.1.3 实现Callable接口--实例
/**
* Callable接口 : 有返回值 可抛出异常
* @author 皮卡丘
*
*/
public class Thread03 implements Callable {
@Override
public Object call() throws Exception {
int s = 0;
for (int i = 0; i < 1000; i++) {
s += i;
}
return s;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> ft = new FutureTask<Integer>(new Thread03());
ft.run();
System.out.println("result: "+ft.get());
}
}
1.2 阻塞线程
1.2.1 join()
public class ThreadBlock extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(this.getName()+"--"+i);
}
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadBlock();
Thread t2 = new ThreadBlock();
Thread t3 = new ThreadBlock();
//线程交叉执行
t1.start();
t1.join();//阻塞其他线程,先执行t1
t2.start();
t3.start();
}
}
1.2.2 yield()
public class ThreadBlock extends Thread {
@Override
public void run() {
for(int i=0;i<10;i++) {
//当前值为偶数,则让出时间片,让其他线程先运行
if(i%2==0) {
yield();
}
System.out.println(this.getName()+"--"+i);
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new ThreadBlock();
t1.setName("V");
Thread t2 = new ThreadBlock();
t2.setName("A");
Thread t3 = new ThreadBlock();
t3.setName("L");
t1.start();
t2.start();
t3.start();
}
}
2.三大特性
2.1 原子性
线程运行步骤不可分割 (例:i++ i-- ++y --y 均不是原子操作)
incrementAndGet 自增并获取值
但是原子性操作基于CAS ,会出现ABA问题(先将A修改为B,在修改回A,此时CAS是无法察觉的)解决方法:版本号+1,在修改前在判断该值,未被修改则修改,否则重新取值进行修改。
public class ThreadAtomic extends Thread {
// public static int x = 0;
public static final AtomicInteger x = new AtomicInteger(0);
public static class AtomicThread extends Thread{
@Override
public void run() {
while(true) {
// x++;
// System.out.println(this.getName()+"--"+x);
System.out.println(this.getName()+"--"+x.incrementAndGet());
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new AtomicThread();
t1.setName("A");
Thread t2 = new AtomicThread();
t2.setName("B");
Thread t3 = new AtomicThread();
t3.setName("C");
t1.start();
t2.start();
t3.start();
}
}
非原子性的执行结果会出现重复,将运行步骤拆分,存在有的线程从第一步进行操作,而有的线程从第二步操作。(例如:x++; 拆分为y = x; y = y +1;两步)
2.2 有序性
程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
public class OrderThreadDemo {
private static volatile OrderThreadDemo instance = null;
private OrderThreadDemo() {}
public static OrderThreadDemo getInstance() {
/**
* 两个线程进入方法
* if instance == null
* 准备创建对象 instance = new OrderThreadDemo();
* 可能分为三个流程:
* 1. 开辟内存空间
* 2. 初始化对象
* 3. 引用和对象绑定
*
* 另外一种方式:
* 1. 开辟内存空间
* 2. 引用和对象绑定 instance--->对象,instance不是null
* 3. 初始化对象(未完成)
*/
if(instance==null) {//判断OrderThreadDemo对象是否存在
instance = new OrderThreadDemo();
}
return instance;
}
}
2.3 可见性
使用volatile关键字(将修改的值立刻更新到主存中,对其他线程有作用 (共享)),即一个线程修改后,通知到所有线程
线程运行将资源复制到工作内存中,在其中修改对其他线程不起作用
public class ThreadVisable extends Thread {
// public static boolean f = false;
public static volatile boolean f = false;
public static class A extends Thread{
@Override
public void run() {
while(true) {
//当f为true时,打印语句
if(f) {
System.out.println(this.getName()+"--"+f);
break;
}
}
}
}
public static class B extends Thread{
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f = true;
System.out.println(this.getName()+"::"+f);
}
}
public static void main(String[] args) {
Thread t1 = new A();
t1.setName("A");
Thread t2 = new B();
t2.setName("B");
t1.start();
t2.start();
}
}
未使用关键字,则一个线程修改后对其他线程无影响-->体现不出可见性
3.CAS
- 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。
- 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成。
- 比较和交换 作为单个原子操作完成, 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败。
当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会挂起,仅是被告知失败,并且允许再次尝试,当然也允许实现的线程放弃操作。 基于这样的原理,CAS 操作即使没有锁,也可以发现其他线程对当前线程的干扰。
4.生产者消费者模型
4.1 概念
生产者和消费者的任务很明确,生产者只管生产数据,然后添加到缓冲队列。而消费者只管从缓冲队列中获取数据。
4.2 代码实现
4.2.1 创建实体类--Food
public class Food{
private String name;
public Food(String name) {
this.name = name;
}
@Override
public String toString() {
return "Food [name=" + name + "]";
}
}
4.2.2 生产者进程 及 消费者进程
public class ProducerThread extends Thread{
private KFC kfc;
public ProducerThread(KFC kfc) {
this.kfc = kfc;
}
@Override
public void run() {
while(true) {
kfc.produce();
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ConsumerThread extends Thread {
private KFC kfc;
public ConsumerThread(KFC kfc) {
this.kfc = kfc;
}
@Override
public void run() {
while(true) {
kfc.consume();
try {
sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4.2.3 定义队列
public class KFC {
//定义缓存事物队列
private Deque<Food> q = new LinkedList<Food>();
private final int MAX_SIZE = 10;
/**
* 生产食物
*/
public synchronized void produce() {
while(q.size()==MAX_SIZE) {
try {
System.out.println("队列已满,生产者休息等待消费者消费");
wait();//阻塞进程 wait()会释放锁,要将方法变为同步方法(加suo)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Food food = new Food("鸡肉饼");
q.offer(food);
System.out.println("生产者生产一个"+food+",还有食物"+q.size());
notify();//欢迎其他的进程
}
/**
* 消费食物
*/
public synchronized void consume() {
while(q.isEmpty()) {//队列为空 消费者不能消费
System.out.println("队列为空,消费者休息,等待生产者生产......");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Food food = q.poll();
System.out.println("消费者,消费一个:"+food+",还有食物"+q.size());
notifyAll();
}
}
4.2.4 调用--Main
public class Main {
public static void main(String[] args) {
KFC kfc = new KFC();
Thread p1 = new ProducerThread(kfc);
Thread p2 = new ProducerThread(kfc);
Thread p3 = new ProducerThread(kfc);
Thread c1 = new ConsumerThread(kfc);
Thread c2 = new ConsumerThread(kfc);
Thread c3 = new ConsumerThread(kfc);
Thread c4 = new ConsumerThread(kfc);
Thread c5 = new ConsumerThread(kfc);
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
c3.start();
c4.start();
c5.start();
}
}
运行结果:
5.线程池
5.1 概念及其优势
线程池:一个线程执行结束后不马上销毁,继续执行新的任务,这样就节省了不断创建线程和销毁线程的开销。
优势:
- 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
- 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执
- 行;
- 方便线程并发数的管控。
- 提供更强大的功能,延时定时线程池
5.2 创建线程池
5.2.1 创建50个线程 Executors.newCachedThreadPool();
public class poolDemo {
public static void main(String[] args) {
int count = 0;
//创建50个线程 10*5
Executor pool = Executors.newCachedThreadPool();
while(count++ < 10) {
pool.execute(()->{
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"::"+i);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
5.2.2 单线程 Executors.newSingleThreadExecutor();
public class poolDemo {
public static void main(String[] args) {
int count = 0;
//单线程运行--一个线程循环执行
Executor pool = Executors.newSingleThreadExecutor();
while(count++ < 10) {
pool.execute(()->{
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"::"+i);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
5.2.3 定义几个即运行几个 Executors.newFixedThreadPool(n);
public class poolDemo {
public static void main(String[] args) {
int count = 0;
//定义几个线程就是几个线程在跑
Executor pool = Executors.newFixedThreadPool(2);
while(count++ < 10) {
pool.execute(()->{
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"::"+i);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
5.3 创建线程池并定时执行任务
public class poolDemo {
public static void main(String[] args) {
ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
//es.scheduleAtFixedRate(command, 延迟时间, 间隔时间, 事件类型);
es.scheduleAtFixedRate(()->{System.out.println("当前时间:"+new Date());}, 0, 1, TimeUnit.SECONDS);
}
}
6.死锁问题
实例:循环等待
public class Resource {
private String name;
public Resource(String name) {
this.name=name;
}
@Override
public String toString() {
return "Resource [name=" + name + "]";
}
}
public class DeadLock implements Runnable {
Resource a = new Resource("A");
Resource b = new Resource("B");
public DeadLock(Resource a,Resource b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
System.out.println("准备锁资源:"+a+"......");
synchronized (a) {
System.out.println("资源:"+a+"锁定成功");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("准备锁资源:"+b+"......");
synchronized (b) {
System.out.println("资源:"+b+"锁定成功");
}
System.out.println("资源"+b+"释放成功");
}
System.out.println("资源"+a+"释放成功");
}
}
public class DeadLockTest {
public static void main(String[] args) {
Resource a = new Resource("A");
Resource b = new Resource("B");
Resource c = new Resource("C");
//线程实现ruannable 定义对象的方法
Thread t1 = new Thread(new DeadLock(a, b));
Thread t2 = new Thread(new DeadLock(b, c));
Thread t3 = new Thread(new DeadLock(c, a));
t1.start();
t2.start();
t3.start();
}
}
执行结果如下所示:
7.synchronized和ReetreeLock区别
synchronized | ReetrantLock |
不需要手动释放锁和开启锁 | 使用灵活,但必须有释放锁动作 |
对象之间是互斥关系 | 手动释放和开启锁 |
只适用于代码块锁 |
实例:票务系统
public class Ticket {
private int num = 100;
private static final ReentrantLock lock = new ReentrantLock();//可重入锁 使用灵活 手动关锁,避免死锁
//卖票
public void sold(String name) {
lock.lock();
try {
int s = --num;
System.out.println(name+",卖出一张票,剩余票数:"+s);
} finally{
lock.unlock();
}
}
//获取剩余票数
public int getNum() {
return num;
}
}
public class TicketThread extends Thread{
private Ticket ticket;
private String name;
public TicketThread(Ticket ticket,String name) {
this.ticket = ticket;
this.name = name;
}
@Override
public void run() {
while(ticket.getNum()>0) {
ticket.sold(name);
try {
sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TicketTest {
public static void main(String[] args) {
Ticket ticket = new Ticket();
TicketThread t1 = new TicketThread(ticket, "A");
TicketThread t2 = new TicketThread(ticket, "B");
TicketThread t3 = new TicketThread(ticket, "C");
t1.start();
t2.start();
t3.start();
}
}
执行结果如下图所示: