线程同步机制:
同步技术的原理:
解决线程安全问题的第二种方法
解决线性安全问题的第二种方法:使用同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法追踪
2.在方法上添上synchronized修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
访问了共享数据的代码
}
private static int ticket = 100;
@Override
public void run() {
//判断票是否存在
while (true) {
if (ticket > 0) {
//提高安全问题出现概率,让程序睡眠
payticketstatic();
}
}
}
public static void payticketstatic(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张票");
ticket--;
}
public static void main(String[] args) {
//创建Runable接口实现对象
RunableImpl run=new RunableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
t0.start();
t2.start();
t1.start();
}
解决线程安全问题-Lock锁
解决线性安全问题的第三种解决方法:使用lock的
java.util.concurrent.locks.lock接口
lock实现了比使用synchronized方法和语句更广泛的操作
lock接口中的方法:
void lock()获取锁
void unlock()释放锁
java.util.concurrent.locks.Reentlock implements lock接口
使用步骤:
1.在成员位置创建一个Renntrantlock对象
2.在可能出现安全问题代码前调用lock接口中的lock锁
3.在可能出现安全问题的代码后调用lock接口的unlock释放锁
private int ticket=100;
Lock l=new ReentrantLock();
@Override
public void run() {
//判断票是否存在
while(true){
l.lock();
if(ticket>0) {
//提高安全问题出现概率,让程序睡眠
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
}
等待唤醒机制
等待与唤醒机制案例概述
等待与唤醒机制需求分析
代码实践
//包子类
public class BaoZi {
String pi;
String xian;
boolean flag=false;
}
/*
生产者(包子铺类):是一个线程类,可以继承Thread
设置线程任务(run):生产包子
对包子的状态进行判断
true:有包子
包子铺使用wait方法进入等待状态
false:没有包子
包子铺生产包子
增加一些趣味性:交替生产两种包子
有两种状态(i%2==0)
包子铺生产好了包子
修改包子状态
唤醒吃货线程,让吃货线程吃包子
注意事项:
包子铺线程和包子线程关系-->通信(互斥)
必须保证两个线程只有一个在执行
锁对象必须保证唯一,可以使用包子对象作为锁对象
包子铺类和吃货对象需要把包子对象作为参数传递进来
1.需要在成员位置创建一个包子变量
2.使用带参数构造方法,为包子变量赋值
*/
public class BaoziPu extends Thread {
//定义包子变量
private BaoZi bz;
//使用带参构造方法,为这个包子变量赋值
public BaoziPu(BaoZi bz) {
this.bz=bz;
}
//设置县城任务(run):生产包子
@Override
public void run() {
int cout=0;
while(true){
//必须保证只有一个在执行
synchronized (bz){
//判断有没有包子
if(bz.flag==true){
try{
bz.wait();
}
catch (Exception e){
System.out.println(e);
}
}
//被唤醒之后执行,包子铺生产包子
//增加趣味性,交替生产两种包子
if(cout%2==0){
//生产 薄皮三鲜馅包子
bz.pi="薄皮";
bz.xian="三鲜馅";
}else{
//生产 冰皮牛肉大葱陷
bz.pi="冰皮";
bz.xian="牛肉大葱";
}
cout++;
System.out.println("包子铺正在生产"+bz.pi+bz.xian);
try {
Thread.sleep(3000);
}catch(Exception e)
{
System.out.println(e);
}
//包子铺生产好包子
//修改包子的状态为true
bz.flag=true;
bz.notify();
System.out.println("包子铺已经生产好了"+bz.pi+bz.xian+"吃货可以开始吃了");
}}
}
public class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(BaoZi bz)
{
this.bz=bz;
}
//吃包子
@Override
public void run() {
while(true){
synchronized (bz){
if(bz.flag==false){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒后执行的代码,吃包子
System.out.println("吃货正在吃"+bz.pi+bz.xian);
//吃货吃完包子
//修改状态
bz.flag=false;
bz.notify();
System.out.println("吃货已经把"+bz.pi+bz.xian+"包子吃完了,包子铺开始生产");
System.out.println("========================================");
}
}
}
}
public static void main(String[] args) {
BaoZi bz=new BaoZi();
new BaoziPu(bz).start();
new ChiHuo(bz).start();
}
线程状态概述
等待与唤醒案例
等待与唤醒案例:线程之间的消费者
创建一个顾客线程(消费者):告知老板要的包子种类和数量,调用wait方法,放弃cpu执行,进入到waiting状态
创建一个老板线程(生产者):花了5秒做包子,做好包子之后带哦用notify方法,唤醒顾客吃包子
注意:
顾客和老板必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
同步使用的锁对象必须保证唯一的
只有锁对象才能调用wait和notify方法
Object类中的方法:
void wait()
在其他线程调用此对象notify()方法或notifyAll()方法前,当前线程等待
void notify()
唤醒在此对象监视器上等待的单个线程
会继续执行wait方法之后的代码
Object obj =new Object();
new Thread(){
@Override
public void run() {
// 保证等待和唤醒的线程只能有一个执行
while(true){
synchronized (obj){
System.out.println("告知老板要的包子种类和数量");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒后执行的代码
System.out.println("包子做好了,开吃");
}
}}
}.start();
new Thread(){
@Override
public void run() {
while(true){ //花5秒做包子
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("老板5秒之后做好包子,告知顾客,可以吃包子了");
obj.notify();
}
}}
}.start();
等待与唤醒案例(计时等待)
进入Timewaiting(即使等待)有两种方式
1.使用sleep(long m)方法,在毫秒值结束后,线程睡醒进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束后,没有被notify唤醒,会自动唤醒
唤醒方法:
void notify()唤醒对象监视器上的单个线程
void notifyAll()唤醒对象监视器上的所有线程
Object obj =new Object();
new Thread(){
@Override
public void run() {
// 保证等待和唤醒的线程只能有一个执行
while(true){
synchronized (obj){
System.out.println("告知老板要的包子种类和数量");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒后执行的代码
System.out.println("包子做好了,顾客2开吃");
System.out.println("================");
}
}}
}.start();
new Thread(){
@Override
public void run() {
// 保证等待和唤醒的线程只能有一个执行
while(true){
synchronized (obj){
System.out.println("告知老板要的包子种类和数量");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒后执行的代码
System.out.println("包子做好了,顾客1开吃");
System.out.println("================");
}
}}
}.start();
new Thread(){
@Override
public void run() {
while(true){ //花5秒做包子
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("老板5秒之后做好包子,告知顾客,可以吃包子了");
obj.notifyAll();
}
}}
}.start();
}}
线程池
线程池的概念
线程池的代码实践
线程池:JDK1.5之后提供的
java.util.concurrent.Executors:线程池的工厂,用来生产线程消息
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用的线程数的线程池
参数:
int nThread:创建线程池中包含的线程数量
返回值:
ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExectorService接口接受(面向接口百编程)
java.util.concurrent.ExecutorService:线程池接口
用来从线程池中获取,调用start方法,执行线程任务
submit(Runnable task)提交一个Runnable任务用于执行
关闭/修会线程池的方法
void shutdown()
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定数量的线程池
2.创建一个类,实现Runnable类接口,重写run方法,设置线程任务
3.调用ExectorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个新线程");
}
}
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
//线程池会一直开启,使用完了线程,会自动归还给线程,线程可以继续使用
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.shutdown();
es.submit(new RunnableImpl()); //线程池都没有了,就不能获取线程了
}
Lambda表达式
Lambda表达式无参无返回值的练习
public interface Cook {
public abstract void makecook();
}
public static void main(String[] args) {
//嗲用invokecook方法,参数是cook接口
InvokeCook(() -> {
System.out.println("吃饭了");
});
}
public static void InvokeCook(Cook cook) {
cook.makecook();
}
}
Lambda表达式有参练习
public class Person {
private String name;
private int age;
}
public static void main(String[] args) {
Person[] arr={
new Person("迪丽热巴",18),
new Person("古力娜扎",28),
new Person("同俩天",38)
};
Arrays.sort(arr,
(Person o1, Person o2)-> {
return o1.getAge()-o2.getAge();
});
for (Person person : arr) {
System.out.println(person);
}
}