线程简介
- 任务
- 多任务(人同时做多件事情,看似同时进行,实际大脑在同一个时间只执行一件事情)
- 多线程(多条路同时执行)

- 程序-进程-线程
- 一个程序开启一个进程,一个进程可以有多个线程(一个播放器进程:声音线程、图像线程、字幕线程)
- Process与Thread
- 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
- 进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
- 在一个进程中可以包含若干个线程,一个进程至少有一个线程,不然进程没有实际存在的意义,线程是CPU调度和执行的单位
- 大多数的多线程是模拟出来的,真正的多线程是有多个cpu(如服务器),如果是模拟出来的多线程,在只有一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,cpu会在线程间进行切换,所有就有了多线程并行执行的错觉
- 核心
- 线程是独立的执行路径
- 在运行程序时,即使没有自己创建线程,后台也会有多个线程(主线程,gc线程(回收机制)等)
- main() 称为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,若开辟了多个线程,线程的运行由调度器安排调度,调度器与操作系统紧密相关,先后顺序是不能人为的干预的
- 多个线程操作同一份资源时会存在资源抢夺问题,需要加入并发控制
- 多线程会带来额外的开销,如cpu调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程实现
- 线程的创建
- Thread class
package com.gaopeng.thread;
public class Demo01 extends Thread{
//重写run方法是为了,在其中编写自己创建线程要运行的方法
public void run(){
test();
}
public void test(){
for (int i = 0; i < 200; i++) {
System.out.println("线程1------"+i);
}
}
public static void main(String[] args) {
//继承了Thread之后自己也有了多线程的特性,可以创建线程使用start方法
Demo01 thread = new Demo01();
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("主程序"+i);
}
}
}
2. Runnable
package com.gaopeng.thread;
public class Demo02 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("Runnable实现多线程----"+i);
}
}
}
package com.gaopeng.thread;
public class Demo03 {
public static void main(String[] args) {
Demo02 demo02 = new Demo02();
Thread thread = new Thread(demo02);
thread.start();
for (int i = 0; i < 500; i++) {
System.out.println("主程序------"+i);
}
}
}
//?为什么Thread也实现了Runable接口,然后new Thread(a);时又传入一个实现Runnbale的实例对象
//模拟抢票,,线程不安全
package com.gaopeng.thread;
public class Demo05 implements Runnable{
private int ticket=10;
@Override
public void run() {
//设置while循环 是为了模拟抢票的环境
while (true){
if(ticket<=0)break;
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
}
}
public static void main(String[] args) {
Demo05 demo05 = new Demo05();
new Thread(demo05,"康康").start();
new Thread(demo05,"Bryan").start();
new Thread(demo05,"Nuoji").start();
//10张票被抢了两次,线程不安全
}
}
//龟兔赛跑
package com.gaopeng.thread;
public class Demo06 implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if(Thread.currentThread().getName().equals("兔子")&& i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
//如果返回结果为false 说明还没有胜利者
Boolean result = gameOver(i);
if (result){
break;
}
}
}
private Boolean gameOver(int steps){
//如果winner不为空 说明有了胜利者
if(winner!=null){
return true;
}else{
//如果winner为空说明没有胜利者 判断是否到达终点,若到达赋值胜利者
if(steps>=100){
winner=Thread.currentThread().getName();
System.out.println(winner + "胜利了");
}
}
return false;
}
public static void main(String[] args) {
Demo06 demo06 = new Demo06();
new Thread(demo06,"兔子").start();
new Thread(demo06,"乌龟").start();
}
}
3. Callable
1. 实现Callable接口,需要返回值类型
2. 重写call方法,需要抛出异常,并设置返回值类型
3. 创建目标对象
4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);//创建线程池,1表示一个线程资源
5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
6. 获取结果:boolean r1 = result1.get();
7. 关闭服务:ser.shutdownNow();
静态代理
- 代理:有人帮你做一些事情
- lambda表达式:创建类实现接口和使用方法简化成-使用方法(但是到最后都没有指定类型了,那么到底实现了什么接口,为何能精准的使用其中的方法(答:指定了实现的类型为接口))
线程状态

线程的礼让(yield)
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功,要看cpu的调度结果。
package com.gaopeng.yield;
public class TestYield {
public static void main(String[] args) {
TestYield1 testYield = new TestYield1();
new Thread(testYield,"a").start();
new Thread(testYield,"b").start();
//运行结果:
/*
* a线程,开始执行
b线程,开始执行
a线程,执行结束
b线程,执行结束
* */
}
}
class TestYield1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程,开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程,执行结束");
}
}
join合并线程(类似于插队)
package com.gaopeng.join;
public class Demo01 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("插队线程"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo01 demo01 = new Demo01();
Thread thread = new Thread(demo01);
thread.start();
for (int i = 0; i < 100; i++) {
if (i==50){
thread.join();//main线程阻塞
}
System.out.println("主线程执行"+i);
}
}
}
获取线程的状态
- new:尚未启动的线程处于此状态
- runnable:在Java虚拟机中执行的线程处于此状态
- blocked:被阻塞等待监听器锁定的线程处于此状态
- waiting:正在等待另一个线程特定动作的线程处于此状态
- timed_waiting:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- terminated:已退出的线程处于此状态
(已完成的线程不能再次start)
package com.gaopeng.status;
public class Demo01 {
//获取线程的状态
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("。。。。。。。");
});
Thread.State state = thread.getState();
System.out.println(state);//new
thread.start();
System.out.println(state);//runnable
while (state!= Thread.State.TERMINATED) {
System.out.println(state);//timed_waiting
state=thread.getState();
}
System.out.println(state);//terminated
}
}
线程的优先级
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行(注意:设置了优先级只是被cpu调度的概率大了一些,并不是一定,有可能cpu会调用优先级的线程就会出现性能倒置)
- 线程的优先级用数字表示,范围1~10
- Thread.NORM_PRIORITY=5;
- 获取优先级,改变优先级
- getPriority() setPriority(int x)
package com.gaopeng.priority;
public class Demo01 {
public static void main(String[] args) {
P p = new P();
Demo01 demo01 = new Demo01();
Thread t = new Thread(p,"main");
Thread t1 = new Thread(p,"1");
Thread t2 = new Thread(p,"2");
Thread t3 = new Thread(p,"3");
Thread t4 = new Thread(p,"4");
Thread t5 = new Thread(p,"5");
Thread t6 = new Thread(p,"6");
t1.setPriority(10);//最高优先级
t6.setPriority(9);
t2.setPriority(1);
t.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class P implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
}
}
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕(main主函数)
- 虚拟机不用等待守护线程执行完毕(gc垃圾回收线程)
- 守护线程:后台记录操作日志,监控内存,垃圾回收等
package com.gaopeng.deamon;
public class Demo01 {
public static void main(String[] args) {
Person person = new Person();
God god = new God();
Thread godThread = new Thread(god);
godThread.setDaemon(true);//默认为false 一般情况都是用户线程
godThread.start();
new Thread(person).start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝永远存在");
}
}
}
class Person implements Runnable{
@Override
public void run() {
for (int i = 0; i < 30000; i++) {
System.out.println("开心的活着,努力奋斗,奋斗");
}
System.out.println("goodbye");
}
}
线程同步
- 多个线程操作同一个资源
- 并发问题:同一个对象被多个线程同时操作,如何保证安全问题
- 解决:队列,多个线程进行排队访问资源
- 线程同步:多个线程访问同一个对象时,某些线程想要修改这个对象,这个时候就需要实现线程同步,线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的线程池形成队列,等待前面的线程使用完毕,下一个线程再进行使用。
- 锁机制:由于同一个进程的多个线程共享同一块存储空间,再带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入了锁机制,synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用之后释放锁,下一个线程在获得锁进行操作。
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换问题,和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
线程的不安全(线程的内存都是自己的,互不影响,(线程操作时将主内存的资源拷贝到自己的内存然后操作)这样操作同一个资源时就会发生问题)
package com.gaopeng.syn;
//案例1:买票不安全
public class Demo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"1").start();
new Thread(ticket,"2").start();
new Thread(ticket,"3").start();
}
}
class Ticket implements Runnable{
private int ticket=10;
private boolean flag=true;
@Override
public void run() {
while (flag){
buy();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void buy(){
if(ticket<=0){
flag=false;
}
System.out.println(Thread.currentThread().getName()+"买了第"+ticket--+"张票");
}
}
package com.gaopeng.syn;
//案例2:取钱不安全
public class Demo02 {
public static void main(String[] args) {
Account account = new Account(100, "爱情基金");
GetMoney getMoney = new GetMoney(account,100,"我");
GetMoney getMoney2 = new GetMoney(account,500,"你");
new Thread(getMoney).start();
new Thread(getMoney2).start();
}
}
class Account{
private int money;
private String name;
public Account(int money,String name){
this.money=money;
this.name=name;
}
public void setMoney(int money) {
this.money = money;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public String getName() {
return name;
}
}
class GetMoney extends Thread{
Account account;
private int getMoney;
private String name;
private int money;
public GetMoney(Account account,int getMoney,String name){
super(name);//父类的构造方法,要写在第一行
this.getMoney=getMoney;
this.account=account;
}
@Override
public void run() {
//取钱,账户金额减少,手里的钱增加
if(account.getMoney()<=0){
System.out.println("钱已取完");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.getMoney()-getMoney);
money=getMoney;
System.out.println("AccountMoney:"+account.getMoney());
System.out.println(this.getName()+"money:"+money);//这里的name获取的是线程的名字,这个类继承了Thread类,可以使用他的方法
}
}
package com.gaopeng.syn;
import java.util.ArrayList;
import java.util.List;
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {list.add(Thread.currentThread().getName().toString());}).start();
}
// Thread.sleep(3000);
System.out.println(list.size());//9916
//两个线程操作同一个list地址时会进行覆盖操作,所以会线程不安全
}
}
使用synchronized优化线程不安全问题
package com.gaopeng.syn2;
//案例1:买票不安全 对代码进行改造加上同步锁
public class Demo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"1").start();
new Thread(ticket,"2").start();
new Thread(ticket,"3").start();
}
}
class Ticket implements Runnable{
private int ticket=10;
private boolean flag=true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(1000);
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void buy(){//变成了同步方法,一个线程操作完了之后其他的线程才能继续操作
if(ticket<=0){
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"买了第"+ticket--+"张票");
}
}
package com.gaopeng.syn2;
//案例2:取钱不安全
public class Demo02 {
public static void main(String[] args) {
Account account = new Account(100, "爱情基金");
GetMoney getMoney = new GetMoney(account,100,"我");
GetMoney getMoney2 = new GetMoney(account,500,"你");
new Thread(getMoney).start();
new Thread(getMoney2).start();
}
}
class Account{
private int money;
private String name;
public Account(int money,String name){
this.money=money;
this.name=name;
}
public void setMoney(int money) {
this.money = money;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public String getName() {
return name;
}
}
class GetMoney extends Thread{
Account account;
private int getMoney;
private String name;
private int money;
public GetMoney(Account account,int getMoney,String name){
super(name);//父类的构造方法,要写在第一行
this.getMoney=getMoney;
this.account=account;
}
@Override
public void run() {
synchronized (account){//锁的对象是变化的对象,是会产生增删改的对象,这里是账户被增删改
//取钱,账户金额减少,手里的钱增加
if(account.getMoney()<=0){
System.out.println("钱已取完");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.getMoney()-getMoney);
money=getMoney;
System.out.println("AccountMoney:"+account.getMoney());
System.out.println(this.getName()+"money:"+money);//这里的name获取的是线程的名字,这个类继承了Thread类,可以使用他的方法
}
}
}
package com.gaopeng.syn2;
import java.util.ArrayList;
import java.util.List;
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized (list) {//哪个对象会增删改,就对他加上锁
list.add(Thread.currentThread().getName().toString());
System.out.println("0");
}
}).start();
}
Thread.sleep(3000);//上面的线程有可能没有全部执行完,所以设置三秒延迟
System.out.println(list.size());//9916
//两个线程操作同一个list地址时会进行覆盖操作,所以会线程不安全
}
}
同步方法synchronized
- 我们需要对方法出一套机制,这套机制就是synchronized关键字,它包括两种用法synchronized方法和synchronized块:同步方法:public synchronized void method(int args){}
- synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程处于阻塞状态,方法一旦执行,就独占这个锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,然后执行方法
- 缺点:若将一个大的方法申明为synchronized将会影响效率
- 例如:

- 同步块:synchronized(Obj){}
- Obj称之为同步监视器
- obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class(反射原理)
- 同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现台同步监视器被锁定,无法访问
- 第一个线程执行完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
并发编程JUC
package com.gaopeng.syn2;
import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {
//自带锁机制的list 用于并发编程
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName()) ;
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
死锁问题
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有"两个以上对象的锁"时,就可能发生死锁问题
- 产生死锁的必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
- 解决方案(不要进行锁嵌套),打破四个必要条件之一或多个就可以避免死锁的发生
- 制造死锁
package com.gaopeng.lock;
public class Demo01 {
public static void main(String[] args) {
new Thread(new Operate(0),"线程0").start();
new Thread(new Operate(1),"线程1").start();
}
}
//lipstick:口红
class Lipstick{
}
//mirror:镜子
class Mirror{
}
class Operate implements Runnable{
//请static修饰,确认资源只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choose;
public Operate(int choose){
this.choose=choose;
}
@Override
public void run() {
try {
operation();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void operation() throws InterruptedException {
if (choose==0){
synchronized (lipstick){
System.out.println(Thread.currentThread().getName()+"获得了口红");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(Thread.currentThread().getName()+"获得了镜子");
}
}
}else{
synchronized (mirror){
System.out.println(Thread.currentThread().getName()+"获得了镜子");
Thread.sleep(2000);
synchronized (lipstick){
System.out.println(Thread.currentThread().getName()+"获得了口红");
}
}
}
}
}
- 解决死锁(不进行锁嵌套)
package com.gaopeng.lock;
public class Demo01 {
public static void main(String[] args) {
new Thread(new Operate(0),"线程0").start();
new Thread(new Operate(1),"线程1").start();
}
}
//lipstick:口红
class Lipstick{
}
//mirror:镜子
class Mirror{
}
class Operate implements Runnable{
//请static修饰,确认资源只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choose;
public Operate(int choose){
this.choose=choose;
}
@Override
public void run() {
try {
operation();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void operation() throws InterruptedException {
if (choose==0){
synchronized (lipstick){
System.out.println(Thread.currentThread().getName()+"获得了口红");
Thread.sleep(1000);
}
synchronized (mirror){
System.out.println(Thread.currentThread().getName()+"获得了镜子");
}
}else{
synchronized (mirror){
System.out.println(Thread.currentThread().getName()+"获得了镜子");
Thread.sleep(2000);
}
synchronized (lipstick){
System.out.println(Thread.currentThread().getName()+"获得了口红");
}
}
}
}
Lock锁
- 显示的添加锁,释放锁
package com.gaopeng.lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket).start();
new Thread(ticket).start();
new Thread(ticket).start();
}
}
class Ticket implements Runnable{
private final ReentrantLock lock = new ReentrantLock();
private int tickeyNum = 10;
@Override
public void run() {
getTicket();
}
void getTicket(){
try {
lock.lock();//添加锁
while (true){
if(tickeyNum>0){
Thread.sleep(1000);
System.out.println(tickeyNum--);
}else {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁,一般使用try catch 将unlock放入finally中
}
}
}
synchronized与Lock的对比
- Lock是显示锁(手动开启和关闭锁,关闭锁一般写入finally中)synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且有更好的扩展性,提供更多的子类
- 优先使用顺序:
- Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(方法体之外)
线程协作(生产者消费者模式-非23种设计模式)
package com.gaopeng.communication;
//测试:生产者消费者模型--->利用缓冲区解决:管程法
//需要对象:生产者,消费者,缓冲区,产品
public class Demo01 {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Customer(synContainer).start();
}
}
//生产者
class Productor extends Thread{
SynContainer synContainer;
public Productor(SynContainer synContainer) {
this.synContainer=synContainer;
}
//生产商品
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new Chicken(i));
System.out.println("生产了,编号为"+i+"的鸡");
}
}
}
class Customer extends Thread{
SynContainer synContainer;
public Customer(SynContainer synContainer) {
this.synContainer=synContainer;
}
//消费商品
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Chicken pop = synContainer.pop();
System.out.println("消费了,编号为"+pop.id+"的鸡");
}
}
}
class Chicken{
int id;
public Chicken(int id){
this.id=id;
}
}
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count=0;
public synchronized void push(Chicken chicken1){//生产者和消费者同时操作容器,需要进行同步操作
//判断容器是否存满了商品
if (count==chickens.length){
//等待消费者消费
try {
this.wait();
// return;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产商品
chickens[count]=chicken1;
count++;
//告知消费者消费
this.notifyAll();
}
public synchronized Chicken pop(){
if (count==0){
//等待生产者生产商品
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费商品
count=count-1;
Chicken chicken2 =chickens[count];
// chickens[count]=null;
this.notifyAll();
return chicken2;
}
}
线程通信问题
//案例2--->信号灯法
package com.gaopeng.communication;
//生产者消费者模式--->信号灯法
public class Demo02 {
public static void main(String[] args) {
TV tv = new TV();
new Actor(tv).start();
new Audience(tv).start();
}
}
//演员
class Actor extends Thread{
TV tv;
public Actor(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0) {
tv.perform("轩辕剑");
System.out.println("表演了轩辕剑");
}else {
tv.perform("天龙八部");
System.out.println("表演了天龙八部");
}
}
}
}
//观众
class Audience extends Thread{
TV tv;
public Audience(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
// System.out.println("观看了"+watch);
}
}
}
//容器
class TV{
private String voice;
private boolean flag=false;
//演员表演
public synchronized void perform(String voice){
if (this.flag){
//等于false时演员表演,通知观众观看
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//演员表演,观众等待
this.voice=voice;
this.flag=!this.flag;
this.notifyAll();
}
}
//观众观看
public synchronized void watch(){
if (this.flag){
//观众观看
System.out.println("观众观看了"+voice);
this.flag=!this.flag;
System.out.println(flag);
this.notifyAll();
}else{
//通知演员表演
try {
System.out.println(flag);
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// return this.voice;
}
}
线程池
- 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免重复创建和销毁,实现重复利用。
- 优点:
- 提高响应速度,节省了重复创建的时间
- 降低资源消耗,节省了重复创建重复销毁的资源
- 便于管理线程
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最长保留时间
package com.gaopeng.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Demo01 {
public static void main(String[] args) {
//创建线程池:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Test());
executorService.execute(new Test());
executorService.execute(new Test());
executorService.execute(new Test());
//应该有如何获取,如何归还等操作,跟数据库连接池一样?
//关闭线程池
executorService.shutdown();
}
}
class Test implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
高级主题