java多线程学习笔记
普通方法和多线程简单图解
多线程的三个特性:原子性、可见性、有序性
原子性:是指一个操作是不可中断的。即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给他赋值为1,线程B给他赋值为-1。那么不管这两个线程
以何种方式。何种步调工作,i的值要么是1,要么是-1.线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。
可见性:是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行来说,可见性问题是不存在的。
有序性:在并发时,程序的执行可能会出现乱序。给人的直观感觉就是:写在前面的代码,会在后面执行。有序性问题的原因是因为程序在
执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。
java多线程三种创建方式
继承Thread类(重点)
TestThread1(Thread简单实现)
1、继承Thread类
2、重写run方法
3、创建线程对象,调用start开启线程
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("111111111");
}
}
public static void main(String[] args) {
//创建线程对象,通过线程对象开启线程
TestThread1 testThread1 = new TestThread1();
testThread1.start();
//main主线程
for (int i = 0; i < 2000; i++) {
System.out.println("22222222");
}
}
}
//运行结果
......
111111111
111111111
111111111
111111111
111111111
111111111
111111111
111111111
22222222
22222222
22222222
......
22222222
22222222
22222222
111111111
111111111
111111111
111111111
111111111
......
线程不一定立即执行,由cpu调度安排
TestThread2(Thread下载图片)
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url;
private String name;
public TestThread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("文件名为:"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
TestThread2 t2 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
TestThread2 t3 = new TestThread2("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("io异常,downloader方法出现问题");
}
}
}
//运行结果
文件名为:2.jpg
文件名为:3.jpg
文件名为:1.jpg
实现Runnable接口(重点)
1、定义MyRunnable类实现Runnable接口
2、实现run方法
3、创建线程对象,调用start开启线程
TestThread3(Runnable简单实现)
public class TestThread3 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("111111111");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象开启线程,实现代理
Thread thread = new Thread(testThread3);
thread.start();
//main主线程
for (int i = 0; i < 2000; i++) {
System.out.println("22222222");
}
}
}
小结
TestThread4(火车票并发问题)
//多个线程同时操作同一个对象
//发现问题:多个线程操作同一个资源情况下线程不安全,数据紊乱
public class TestThread4 implements Runnable{
private int ticket=10;
@Override
public void run() {
while (true){
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
//模拟每个线程做完一次之后的延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
new Thread(testThread4,"aa").start();
new Thread(testThread4,"bb").start();
new Thread(testThread4,"cc").start();
}
}
//运行结果
aa拿到了第10张票
bb拿到了第9张票
cc拿到了第8张票
bb拿到了第7张票
aa拿到了第6张票
cc拿到了第5张票
bb拿到了第4张票
aa拿到了第3张票
cc拿到了第2张票
bb拿到了第0张票
aa拿到了第1张票
Race(多线程模拟龟兔赛跑)
//多线程模拟龟兔赛跑
public class Race implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (i>0&&i%10==0&&Thread.currentThread().getName()=="兔子"){
System.out.println("兔子睡了一觉");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有胜者,则终止所有比赛
if (winner!=null){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了第"+i+"步");
//判断是否有胜者可以结束比赛
if (gameOver(i)){
break;
}
}
}
//判断是否完成比赛
private Boolean gameOver(int i){
if (i>=100){
winner=Thread.currentThread().getName();
System.out.println("胜者是"+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
//运行结果
乌龟跑了第39步
兔子跑了第8步
兔子跑了第9步
兔子睡了一觉
乌龟跑了第40步
乌龟跑了第41步
......
乌龟跑了第97步
乌龟跑了第98步
乌龟跑了第99步
乌龟跑了第100步
胜者是乌龟
实现Callable接口(扩展)
1、创建执行服务
2、提交执行
3、获取结果
4、关闭服务
优点:可以返回值,可以跑出异常
TestCallable(Callable下载图片)
//实现Callable接口
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
TestCallable t2 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
TestCallable t3 = new TestCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
//创建执行服务
ExecutorService executorService =Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1=executorService.submit(t1);
Future<Boolean> r2=executorService.submit(t2);
Future<Boolean> r3=executorService.submit(t3);
//获取结果
Boolean rs1=r1.get();
Boolean rs2=r2.get();
Boolean rs3=r3.get();
//关闭服务
executorService.shutdownNow();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("io异常,downloader方法出现问题");
}
}
}
Lambda表达式
TestLambda1(Lambda表达式与其他方式对比)
public class TestLambda1 {
//3、静态内部类
static class Like2 implements ILike{
@Override
public void Lambda() {
System.out.println("i like Lambda2");
}
}
public static void main(String[] args) {
Like like = new Like();
like.Lambda();
Like2 like2 = new Like2();
like2.Lambda();
//4、局部内部类
class Like3 implements ILike{
@Override
public void Lambda() {
System.out.println("i like Lambda3");
}
}
Like3 like3 = new Like3();
like3.Lambda();
//5、匿名内部类,没有类名,必须借助接口或父类
ILike like4 = new ILike() {
@Override
public void Lambda() {
System.out.println("i like Lambda4");
}
};
like4.Lambda();
ILike like5 = () -> {
System.out.println("i like Lambda4");
};
}
}
//1、定义接口
interface ILike{
//接口中只包含一个抽象方法,称为函数式接口,只有函数式接口可以使用Lambda表达式
void Lambda();
}
//2、实现类
class Like implements ILike{
@Override
public void Lambda() {
System.out.println("i like Lambda");
}
}
//运行结果
i like Lambda
i like Lambda2
i like Lambda3
i like Lambda4
TestLambda2(Lambda表达式各种简化)
public class TestLambda2 {
public static void main(String[] args) {
//局部内部类
class Love1 implements ILove{
@Override
public void Lambda(int a) {
System.out.println("I love Lambda"+a);
}
}
ILove love=new Love1();
love.Lambda(1);
//匿名内部类
ILove love2=new ILove() {
@Override
public void Lambda(int a) {
System.out.println("I love Lambda"+a);
}
};
love2.Lambda(2);
//Lambda表达式
ILove love3=(int a)->{
System.out.println("I love Lambda"+a);
};
love3.Lambda(3);
//Lambda表达式 简化括号和数据类型,多个参数不可省略括号
ILove love4=a->{
System.out.println("I love Lambda"+a);
};
love4.Lambda(4);
//Lambda表达式 简化花括号,多个语句不可省略花括号
ILove love5=a-> System.out.println("I love Lambda"+a);
love5.Lambda(5);
//使用Lambda表达式创建Runnable对象
Runnable runnable=()->{
System.out.println("runnable");
};
new Thread(runnable).start();
}
}
interface ILove{
void Lambda(int a);
}
```java
//运行结果
I love Lambda1
I love Lambda2
I love Lambda3
I love Lambda4
I love Lambda5
runnable
静态代理
1、目标对象和代理对象都要实现同一个接口
2、代理对象代理目标对象
优点:
1、代理对象可以做目标对象无法做的事
2、目标对象可以专注自己做的事
StaticProxy(对比Thread对象实现Runnable对象)
//静态代理
public class StaticProxy {
public static void main(String[] args) {
//静态代理是Thread对象实现代理Runnable对象的原理
new Thread(()-> System.out.println("Thread对象代理Runnable对象")).start();
new WeddingCompany(new You()).HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//目标对象
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚");
}
}
//代理对象
class WeddingCompany implements Marry{
//代理->目标对象
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
System.out.println("布置");
this.target.HappyMarry();
System.out.println("结束");
}
}
//运行结果
布置
结婚
结束
线程状态
线程停止(Stop)
1、建议线程正常停止----利用次数,不建议死循环
2、建议使用标志位----设置一个标志位
3、不要使用stop或者destroy等过时的jdk不建议使用的方法
TestStop(自定义线程停止方法)
public class TestStop implements Runnable{
//1、设置一个标志位
private Boolean flag=true;
@Override
public void run() {
int i=0;
while(flag) {
System.out.println("run...Thread"+i++);
}
if (flag==false){
System.out.println("Thread stop");
}
}
//2、设置公开方法停止线程,改变标志位
public void stop(){
flag=false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
//调用stop方法改变标志位,让线程停止
testStop.stop();
}
}
}
}
//运行结果
main897
main898
main899
main900
run...Thread650
main901
main902
main903
main904
main905
main906
main907
main908
main909
main910
main911
Thread stop
main912
main913
main914
main915
线程休眠(Sleep)
TestSleep2(模拟倒计时)
//模拟倒计时
public class TestSleep2 {
private static int time=10;
static public void tenDown() throws InterruptedException {
while(true){
//获取系统当前时间
Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
//输出后延时一秒
Thread.sleep(1000);
//更新当前时间
date = new Date(System.currentTimeMillis());
if (time--<=0){
break;
}
}
}
public static void main(String[] args) throws InterruptedException {
tenDown();
}
}
//运行结果
19:11:57
19:11:58
19:11:59
19:12:00
19:12:01
19:12:02
线程礼让(Yield)
TestYield(测试礼让)
//测试礼让
public class TestYield {
public static void main(String[] args) {
new Thread(new Yield(),"a").start();
new Thread(new Yield(),"b").start();
}
}
class Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程强制执行(Join)
TestJoin(测试线程强制执行)
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("vip线程");
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main线程"+i);
if (i==200){
//主线程的代码块中,如果碰到了thread.join()方法,此时主线程需要等待(阻塞),等待子线程结束了,才能继续执行t.join()之后的代码块。
thread.join();
}
}
}
}
//运行结果
198
199
200
vip
vip
vip
vip
......
vip
vip
201
202
203
204
205
观察线程状态
TestState(观察测试线程状态)
//观察测试线程状态
public class TestState{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
//当前子线程休眠
Thread.sleep(1000);
//线程休眠后输出线程状态:Runnable
System.out.println(Thread.currentThread().getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束");
});
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
while (thread.getState()!=Thread.State.TERMINATED){
//当前主线程休眠
Thread.sleep(500);
//主线程每500毫秒输出一次子线程的状态
System.out.println(thread.getState());
}
}
}
//运行结果
NEW
RUNNABLE
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
TIMED_WAITING
TIMED_WAITING
RUNNABLE
线程结束
TERMINATED
线程优先级
TestPriority(测试线程优先级)
//测试线程优先级
public class TestPriority {
public static void main(String[] args) {
//获取当前主线程的优先级
Thread.currentThread().getPriority();
Thread t1 = new Thread(() -> {
//输出当前子线程的优先级
System.out.println("线程优先级为"+Thread.currentThread().getPriority());
});
Thread t2 = new Thread(() -> {
System.out.println("线程优先级为"+Thread.currentThread().getPriority());
});
Thread t3 = new Thread(() -> {
System.out.println("线程优先级为"+Thread.currentThread().getPriority());
});
Thread t4 = new Thread(() -> {
System.out.println("线程优先级为"+Thread.currentThread().getPriority());
});
//不能超过最大优先级10和最小优先级1
t1.setPriority(7);
t2.setPriority(9);
t3.setPriority(2);
t4.setPriority(10);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//运行结果
线程优先级为7
线程优先级为9
线程优先级为10
线程优先级为2
守护线程
TestDaemon(测试守护线程)
public class TestDaemon {
public static void main(String[] args) {
Thread God = new Thread(new God());
Thread You = new Thread(new You());
God.setDaemon(true);//false为用户线程
God.start();
You.start();
}
}
//守护线程
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝守护着你");
}
}
}
//用户线程
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 35600; i++) {
System.out.println("活着");
if(i==35600){
System.out.println("goodbye world");
}
}
}
}
//运行结果
活着
活着
活着
活着
上帝守护着你
上帝守护着你
上帝守护着你
goodbye world
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
上帝守护着你
线程同步
不安全的例子
UnsafeBuyTicket(买票的线程不安全)
//买票线程不安全
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"aaa").start();
new Thread(buyTicket,"bbb").start();
new Thread(buyTicket,"ccc").start();
}
}
class BuyTicket implements Runnable{
private int ticket=10;
Boolean flag=true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了票"+ticket--);
//判断是否有票
if (ticket<=0){
flag=false;
}
}
}
}
//运行结果
aaa抢到了票10
ccc抢到了票9
bbb抢到了票8
ccc抢到了票7
bbb抢到了票5
aaa抢到了票6
ccc抢到了票3
aaa抢到了票4
bbb抢到了票2
aaa抢到了票1
ccc抢到了票0
bbb抢到了票-1
UnsafeList(集合的线程不安全)
//测试集合的线程不安全
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
//运行结果
//同时add可能会造成覆盖
999867
同步方法
SafeBuyTicket(买票的线程安全)
//线程安全的买票
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"aaa").start();
new Thread(buyTicket,"bbb").start();
new Thread(buyTicket,"ccc").start();
}
}
class BuyTicket implements Runnable{
private int ticket=10;
Boolean flag=true;
@Override
public void run() {
while (flag){
if (ticket<=0){
flag=false;
break;
}
//在开始方法前需要先获取锁
buy();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized同步方法,锁this,即锁该类的实例对象,当前线程改变ticket值后才释放对象的锁,其他线程阻塞排队等待该线程执行完同步方法后才能拿到锁
synchronized public void buy(){
System.out.println(Thread.currentThread().getName()+"抢到了票"+ticket--);
}
}
//运行结果
aaa抢到了票10
bbb抢到了票9
ccc抢到了票8
aaa抢到了票7
ccc抢到了票6
bbb抢到了票5
aaa抢到了票4
bbb抢到了票3
ccc抢到了票2
aaa抢到了票1
同步块
同步块和同步方法相同,同步块可以是任意对象,同步对象是当前类对象,相当于synchronized(this){},是对指定对象加锁和对this对象加锁的区别
SafeList(集合的线程安全)
//测试集合的线程安全
public class SafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
//运行结果
1000000
TestJUC(JUC安全类型的集合)
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
//是个安全的集合类型
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 100000; i++) {
new Thread(()->{
copyOnWriteArrayList.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(copyOnWriteArrayList.size());
}
}
//运行结果
1000000
死锁
DeadLock(死锁例子)
public class DeadLock {
public static void main(String[] args) {
new Thread(new Play(0,"aa")).start();
new Thread(new Play(1,"bb")).start();
}
}
class Headphone{
}
class Discman{
}
class Play implements Runnable{
//需要的资源只有一份,用static来保证只有一份
static Headphone headphone=new Headphone();
static Discman discman=new Discman();
int choice;//选择
String name;//用户
public Play(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
play();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void play() throws InterruptedException {
if (choice==0){
synchronized (headphone){//获得耳机的锁
System.out.println(name+"拿到了耳机");
Thread.sleep(1000);
synchronized (discman){//1秒后想获得前端的锁
System.out.println(name+"拿到了前端");
}
}
}
else{
synchronized (discman){//获得前端的锁
System.out.println(name+"拿到了前端");
Thread.sleep(2000);
synchronized (headphone){//2秒后想获得耳机的锁
System.out.println(name+"拿到了耳机");
}
}
}
}
}
aa拿到了耳机
bb拿到了前端
(死锁无法结束线程)
Lock锁
TestLock(测试Lock锁)
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2,"aa").start();
new Thread(testLock2,"bb").start();
new Thread(testLock2,"cc").start();
}
}
class TestLock2 implements Runnable{
int ticket=10;
private final ReentrantLock reentrantLock=new ReentrantLock();
@Override
public void run() {
while(true){
//除了拿到锁的线程,其余锁在该语句前等待解锁拿资源
reentrantLock.lock();
try {
if (ticket>0) {
System.out.println(Thread.currentThread().getName() + "---->" + ticket--);
}else{
break;
}
}
finally {
reentrantLock.unlock();
}
//抢完票解锁后延迟200毫秒
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//运行结果
aa---->10
bb---->9
cc---->8
aa---->7
bb---->6
cc---->5
bb---->4
aa---->3
cc---->2
bb---->1
线程协作
synchronized和wait/notify的区别:
synchronized可以阻止并发更新同一个共享资源,当同一个资源被一个线程获得锁时,其他线程需要等待该线程释放锁;
wait/notify可以对同一个对象进行逻辑处理,对对象进行手动的线程阻塞并释放锁 和 唤醒解除线程阻塞。
比如当生产者发现产品已满则需要wait等待消费者消费产品,当消费者消费产品后用notify释放锁继续生产产品,反之亦然。
1、有synchronized的地方不一定有wait,notify
2、有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。
3、调用wait()方法前的判断最好用while,而不用if;while可以实现被wakeup后thread再次作条件判断;而if则只能判断一次。
4、调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。
5、调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
6、除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
管程法
TestPC(生产者与消费者)
//生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Producer producer = new Producer(synContainer);
Customer customer = new Customer(synContainer);
new Thread(producer, "a1").start();
new Thread(producer, "a2").start();
new Thread(customer, "b1").start();
new Thread(customer,"b2").start();
}
}
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
class Producer implements Runnable {
private int i = 0;
SynContainer synContainer;
public Producer(SynContainer synContainer) {
this.synContainer = synContainer;
}
public void run() {
while (i <= 200) {
//在调用该方法前需要获得该对象的锁
push();
}
}
//如果需要对run方法里语句进行加锁,可以把语句中需要加锁的语句定义包装成方法调用
private synchronized void push(){
if(i<=200) {
synContainer.push(new Chicken(i));
System.out.println(Thread.currentThread().getName() + "生产了" + (i));
i++;
}
}
}
class Customer implements Runnable {
SynContainer synContainer;
public Customer(SynContainer synContainer) {
this.synContainer = synContainer;
}
public void run() {
for (int i = 0; i < 100; i++) {
pop();
}
}
private synchronized void pop(){
System.out.println(Thread.currentThread().getName() + "消费了" + synContainer.pop().id);
}
}
class SynContainer {
//容器大小为10,最多可以放10个chicken对象
Chicken[] chickens = new Chicken[10];
//当前容器计数器
int count = 0;
//需要synchronized对对象锁上后才能调用wait/notify方法进行阻塞释放锁或者唤醒解除阻塞
public synchronized void push(Chicken chicken) {
//用while不要用if,因为当多个线程同时进入等待的时候,notifyAll唤醒对象上所有wait方法的线程,
// 而如果用的if方法,进入的判断后wait被唤醒后就不会再一次进行判断,有可能会造成不满足条件的线程
// 没有进行判断wait直接执行方法,而while会在线程被唤醒后再次判断,如果被其他线程方法抢先执行了,
// 则其他被唤醒的线程会继续被判断wait等待
while (count == chickens.length) {
//等待消费者消费
System.out.println(Thread.currentThread().getName()+"产品满了");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
this.notifyAll();
}
public synchronized Chicken pop() {
while(count<=0){
//等待生产者生产
try {
//表示线程一直等待直到通知,与sleep的区别是会释放锁
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Chicken chicken = chickens[count - 1];
count--;
//唤醒同一个对象上所有调用wait方法的线程
this.notifyAll();
//随机唤醒一个等待状态的线程
//this.notify();
return chicken;
}
}
//运行结果
a1生产了0
a1生产了1
a1生产了2
a1生产了3
a1生产了4
a1生产了5
a1生产了6
a1生产了7
a1生产了8
a1生产了9
a1产品满了
b1消费了9
......
a1生产了197
b1消费了190
a1生产了198
b1消费了198
a1生产了199
b1消费了199
a1生产了200
b1消费了200
信号灯法
可以理解为缓冲区为1的管程法,生产者生产1然后等待,消费者消费1然后等待,来回重复,只是用flag的true和false标志位代替了缓冲区。
线程池
TestPool
//测试线程池
public class TestPool {
public static void main(String[] args) {
//1、创建服务,创建线程池
//newFixedThreadPool 参数是线程池大小
ExecutorService service= Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//运行结果
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-5
pool-1-thread-4