JavaSE学习笔记 Day22
个人整理非商业用途,欢迎探讨与指正!!
« 上一篇
文章目录
···
18.7线程的等待状态
初始状态:
就绪状态:
运行状态:
等待状态:
限期等待:有时间的等待,Thread.sleep() 或者 join(非0) 等待时间到就回到就绪状态
无限期等待:一直等待,join(0)或者join() 条件满足之后才会回到就绪状态
终止状态:
18.8线程安全
共享的资源(同一个对象),一次只能被一个线程所访问,可以保证我们的数据准确性
18.8.1线程同步的实现
同步代码块
public class Test01 {
public static void main(String[] args) {
// 创建的对象
TicketRunnable tr = new TicketRunnable();
// 创建多个线程去访问同一个资源
Thread t1 = new Thread(tr,"窗口1");
Thread t2 = new Thread(tr,"窗口2");
Thread t3 = new Thread(tr,"窗口3");
Thread t4 = new Thread(tr,"窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketRunnable implements Runnable{
int ticket = 100;
final Object obj = new Object();
@Override
public void run() {
while(true) {
// 使用同步代码块
synchronized (obj) {//互斥锁对象(可以是任意的java对象,但是要保证是唯一的对象)
// 死循环需要程序的出口
if(ticket < 0) {
break;
}
// 200毫秒买票一张
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 进行放票操作
System.out.println(Thread.currentThread().getName()+"卖出了"+ ticket-- +"号票");
}
}
}
}
同步方法
public class Test02 {
public static void main(String[] args) {
// 创建的对象
TicketRunnable1 tr = new TicketRunnable1();
// 创建多个线程去访问同一个资源
Thread t1 = new Thread(tr,"窗口1");
Thread t2 = new Thread(tr,"窗口2");
Thread t3 = new Thread(tr,"窗口3");
Thread t4 = new Thread(tr,"窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketRunnable1 implements Runnable{
int ticket = 100;
// 同步的方法,run被同步了,不是某个资源了
@Override
public void run() {
while(true) {
sale();
}
}
// 锁定的方法是sale
public synchronized void sale() {
// 死循环需要程序的出口
if(ticket < 0) {
System.out.println("票买完了");
// System.exit(0);//停止JVM
// return;
throw new RuntimeException("没票了");
}
// 200毫秒买票一张
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 进行放票操作
System.out.println(Thread.currentThread().getName()+"卖出了"+ ticket-- +"号票");
}
}
18.9线程的阻塞状态
线程的生命周期:
初始状态:
就绪状态:
运行状态:
等待状态:
阻塞状态:调用使用synchronized修饰的方法,就进入了阻塞状态,释放锁时从阻塞状态进入到就绪状态
终止状态:
线程的五大状态:初始状态—>就绪状态—>运行状态—>阻塞/等待状态—>终止状态
18.10线程死锁
当第一个线程拥有A对象并锁定,等待B对象.同时第二线程拥有B对象并锁定,等待A对象
死锁在程序的运行中发生的概率几乎为0
public class Test03 {
public static void main(String[] args) {
BigLeft bl = new BigLeft();
BigRight br = new BigRight();
bl.start();
br.start();
}
}
class MyLock {
static Object objA = new Object();//A对象
static Object objB = new Object();//B对象
}
class BigLeft extends Thread {
@Override
public void run() {
synchronized (MyLock.objA) {
System.out.println("大左拿到了对象A");
synchronized (MyLock.objB) {
System.out.println("大左在等待对象B");
}
}
}
}
class BigRight extends Thread {
@Override
public void run() {
synchronized (MyLock.objB) {
System.out.println("大右拿到了对象B");
synchronized (MyLock.objA) {
System.out.println("大左在等待对象A");
}
}
}
}
18.11线程通信(理解)
18.11.1生产者和消费者
若干个生产者可以生成产品,将生成的产品提供给若干的消费者去消费。生产者和消费者是并发执行的,在两者之间设置一个可以存储多个的产品缓冲区,用于将生产者的产品放入到缓冲中,消费者将缓冲中的产品进行消费,需要保持生产者和消费者之间的一个同步
需要使用的Object中的方法:wait(),notify(),notifyAll()
提示:Object中的等待,唤醒方法都必须在同步的代码块中
package com.qf.test03;
public class Test01 {
// 共享的商场
static Shop shop = new Shop();
public static void main(String[] args) {
ConThread ct = new ConThread();
PhoneThread pt = new PhoneThread();
ct.start();
pt.start();
}
}
/**
* 商品
* */
class Phone {
String name;
}
/**
* 缓冲区
* */
class Shop {
// 表示买卖的商品 若为null需要进行生成 若不null需要进行出售
Phone phone;
// 进货(生产)
public synchronized void putPhone(Phone phone) {//参数表示进货的商品
// 有货
if(this.phone != null) {
try {
System.out.println("当前有货,需要等待消费");
this.wait();//等待消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 表示没有货
System.out.println("正在生产手机:"+phone.name);
// 模拟生产者
this.phone = phone;
// 唤醒其他线程 通知消费者
this.notify();
}
// 卖货(消费)
public synchronized void getPhone() {
// 没有货,需要提醒生产
if(this.phone == null) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 表示要消费了
System.out.println("正在消费:"+this.phone.name);
// 模拟消费
this.phone = null;
// 唤醒其他线程 通知生产者
this.notify();
}
}
/**
* 消费者
* */
class ConThread extends Thread {
@Override
public void run() {
for(int i = 1;i<=10;i++) {
// 消费的操作
Test01.shop.getPhone();
}
}
}
/**
* 生成者
* */
class PhoneThread extends Thread {
@Override
public void run() {
for(int i = 1;i<=10;i++) {
Phone phone = new Phone();
phone.name = "IPhone" + i;
// 生成操作
Test01.shop.putPhone(phone);
}
}
}
18.12本地线程
可以完成数据的共享
数据共享:
1.使用synchronized进行代码的同步
2.使用本地线程ThreadLocal
public class Test01 {
public static void main(String[] args) {
// 本地线程中的数据可以完成共享,每次取出都是同一个数据
ThreadLocal<Dog> t = new ThreadLocal<>();
// 添加方法
t.set(new Dog());
// 获取方法 无论取多少次,都是同一个对象
Dog dog1 = t.get();
Dog dog2 = t.get();
Dog dog3 = t.get();
// 移除方法
t.remove();
Dog dog4 = t.get();
// 再添加
t.set(new Dog());
Dog dog5 = t.get();
Dog dog6 = t.get();
Dog dog7 = t.get();
System.out.println(dog1 == dog2);
System.out.println(dog2 == dog3);
System.out.println(dog4);
System.out.println(dog1 == dog4);
System.out.println(dog1 == dog5);
System.out.println(dog5 == dog6);
System.out.println(dog7 == dog6);
}
}
class Dog {
}
18.13线程池
若有非常多的任务需要使用线程来完成,并且线程的使用时间都不是很长,这样频繁的去创建和销毁线程会比较销毁性能,可以使用线程池来完成这样的任务,线程池可以完成线程的重用
线程中有一个线程队列,队列中的每个线程都是处于空闲状态,每次就不用重新创建了
18.13.1线程池的常见类和接口
Executor:线程池的顶级接口
ExecutorSevice:线程池接口,可通过summit()提交任务代码
Executors工厂类:通过此类去创建一个线程池对象
newFixedThreadPool(int nThreads):获取固定数量的线程池,参数就是线程的个数
newCechedThreadPool():获取动态数量的线程池,若不够就自行的创建,无上限
public class Test01 {
public static void main(String[] args) {
// 1.获取固定个数的线程池,个数为3
ExecutorService es = Executors.newFixedThreadPool(3);
// 2.提交线程池需要帮助我们完成的任务(Runnable的实现类),submit方法可以自动的执行run
es.submit(new A());
es.submit(new A());
es.submit(new A());
// 先执行完前三个线程的任务,执行完毕后再进行第四个的执行
es.submit(new A());
// 3.关闭线程池:当所有的资源都执行完毕时,关闭线程池
es.shutdown();
}
}
// 创建线程任务类
class A implements Runnable {
@Override
public void run() {
for(int i = 1;i<=10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread()+":"+i);
}
}
}
18.13.2Callable接口
JDK1.5后加入的,与Runnable接口类似,实现后表示一个线程任务
Callable具有泛型,可以声明异常
public class Test02 {
public static void main(String[] args) {
// 可以任意添加任务,无需设定线程的数量
ExecutorService es = Executors.newCachedThreadPool();
es.submit(new B());
es.submit(new B());
es.submit(new B());
es.submit(new B());
es.shutdown();
}
}
// 创建线程任务类
class B implements Callable<String> {//此泛型规定了方法的返回值
@Override
public String call() throws Exception {
for(int i = 1;i<=100;i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread()+":"+i);
}
return null;
}
}
18.13.3Future接口
表示将要执行完的任务的结果
get()方法以阻塞的形式,等待Future中异步处理结果(call的返回值)
public class Test03 {
public static void main(String[] args) throws Exception {
// 可以任意添加任务,无需设定线程的数量
ExecutorService es = Executors.newCachedThreadPool();
Future<Integer> submit = es.submit(new C());
System.out.println(submit.get());
es.shutdown();
}
}
// 创建线程任务类
class C implements Callable<Integer> {//此泛型规定了方法的返回值
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1;i<=100;i++) {
sum += i;
}
return sum;
}
}
public class Test04 {
// 计算1~1000000的和 使用4个任务去完成
// 任务1:1~250000 任务2:250001~500000 任务3:500001~750000 任务4:750001~1000000
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 创建线程池对象
ExecutorService es = Executors.newCachedThreadPool();
Future<Integer> f1 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1;i<=250000;i++) {
sum += i;
}
return sum;
}
});
Future<Integer> f2 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 250001;i<=500000;i++) {
sum += i;
}
return sum;
}
});
Future<Integer> f3 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 500001;i<=750000;i++) {
sum += i;
}
return sum;
}
});
Future<Integer> f4 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 750001;i<=1000000;i++) {
sum += i;
}
return sum;
}
});
System.out.println(f1.get() + f2.get() + f3.get() + f4.get());
es.shutdown();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
18.14Lock锁
在JDK1.5后加入,与synchronized比较,显示定义,结构更加的灵活
public class Test01 {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
TicketRun run = new TicketRun();
es.submit(run);
es.submit(run);
es.submit(run);
es.shutdown();
}
}
class TicketRun implements Runnable {
int ticket = 100;
// 创建Lock对象
// 互斥锁和synchronized 功能一样
Lock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try {
// 上锁(添加锁)
lock.lock();
if(ticket < 0) {
break;
}
System.out.println(Thread.currentThread()+"--->卖出了"+ ticket-- +"号票");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//开锁(释放锁) 无论如何都要进行释放
lock.unlock();
}
}
}
}
公平锁:
让每个线程都公平的去执行当前的任务
非公平锁:
优先让上一个线程去完成接下来的任务
synchronized:非公平锁,互斥锁,同一个时刻只允许一个线程只有锁,当有一个线程持有锁,其他线程都会进入到阻塞状态直到锁被释放
// 参数为true时公平锁 false时非公平锁 默认为非公平的
Lock lock = new ReentrantLock(true/false);