主要内容
[1]线程同步
[2]生产者与消费者
[3]实现多线程其它方式,线程池,线程组
23.1为什么需要线程的同步?
多线程在访问共享资源时,容易出现“数据错乱/脏数据”的情况。
同步:完成一个功能,需要N多句代码组成,这N多句代码必须一起执行完毕,才代表一个功能结束,在完成这个功能的过程中,代码不允许被“打断”,同步
同步"锁"
(1)实现同步方式
1.同步代码块
package com.bjsxt.titcket;
public class Ticetk2 implements Runnable{//Ticetk这个类具备了一个能力,多线程操作的能力
private int ticket=5;//5张票
@Override
public void run() {
//线程体
for (int i = 0; i <100; i++) {
//同步代码块
synchronized (this) {//小括号中,共享资源的对象
if (ticket>0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票");
ticket--;
}
}
}
}
}
测试:
package com.bjsxt.titcket;
public class TestTicket {
public static void main(String[] args) {
//创建线程类的对象
Ticetk2 t=new Ticetk2(); //5张票
//创建三个线程代理类
Thread t1=new Thread(t,"A窗口");
Thread t2=new Thread(t,"B窗口");
Thread t3=new Thread(t,"C窗口");
//启动线程
t1.start(); //for--0--100
t2.start();//for--0--100
t3.start();
}
}
2.同步方法
package com.bjsxt.titcket;
public class Ticetk3 implements Runnable{//Ticetk这个类具备了一个能力,多线程操作的能力
private int ticket=5;//5张票
@Override
public void run() {
//线程体
for (int i = 0; i <100; i++) {
//调用
saleTicket();//卖票的方法
}
}
//同步方法 -->锁的是当前类的对象this
private synchronized void saleTicket(){
if (ticket>0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票");
ticket--;
}
}
}
测试:
package com.bjsxt.titcket;
public class TestTicket {
public static void main(String[] args) {
//创建线程类的对象
//Ticetk2 t=new Ticetk2(); //5张票
Ticetk3 t=new Ticetk3();
//创建三个线程代理类
Thread t1=new Thread(t,"A窗口");
Thread t2=new Thread(t,"B窗口");
Thread t3=new Thread(t,"C窗口");
//启动线程
t1.start(); //for--0--100
t2.start();//for--0--100
t3.start();
}
}
“锁”的是什么?共享资源对象 被锁住的对象,称为“同步监视器”
同步代码块 -->同步监视器,可以是共享资源的对象,也可以是当前类对象this
同步方法 -->同步监视器,,,只能是当前类的对象this
同步可以提高程序的安全性
过多的同步,就会产生死锁,双方都在等待对方的资源
按照“boss”的算法,计算大小,大的先获取锁
23.2生产者与消费者问题
(1)数据错乱 (旺仔矿泉水,娃哈哈小馒头) 同步
(2)重复生产和重复取走 (线程间的通信)
等待:wait()
唤醒notify(),notifyA()
以上方法均为Object类中的方法,可以被子类调用,但不能被子类重写,因为均为final修饰的方法
线程间通信的方法,只能用在同步代码块或同步方法中
sleep(...)与wait()的区别
sleep与wait都会导致线程进入阻塞状态
(1)sleep方法为Thread类中的static方法 ,wait()Object类中的final方法
(2)sleep()不会释放对象锁,wait()会释放对象锁,等待池,当notify()或notifyAll()唤醒进入锁池,就绪状态
(3)wait()等待,当被唤醒后,从wait()之后的代码开始执行
if(){
wait();
}
....... wait()之后的代码
run()方法有没有局限性? 有 ,因为如果有异常,必须处理,不允许声明
void,如果希望方法运行之后有结果怎么办?
在Java中JDK1.5开始,实现多线程的方式,有三种,实现Callable接口
call()方法的好处
(1)有返回值
(2)可以抛出异常
(3)支持泛型
package com.bjsxt.callable;
import java.util.concurrent.Callable;
public class RandomCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return (int)(Math.random()*10);//0-9
}
}
测试:
package com.bjsxt.callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建任务
RandomCallable rc=new RandomCallable();
//任务管理器 -->Runnable的实现类不?是,线程对象是?是
FutureTask<Integer> ft=new FutureTask<>(rc);
//创建代理类的对象
Thread t=new Thread(ft); //多态,接口作方法的形式参数,可以传入Runnable的任务实现类
/**判断任务是否执行完成*/
System.out.println("任务是否执行完毕"+ft.isDone());
//才会执行call()方法中的代码
t.start();
/**在主线程中获取结果,这个时候 call()方法未必执行完,有可能执行到获取结果时,而结果还没有计算出来*/
System.out.println("获取结果:"+ft.get()); //导致后面的代码无法执行
System.out.println("任务是否执行完毕"+ft.isDone());
}
}
