线程
线程和进程之间的区别:
进程:每一进程都有自己独立的代码和数据空间, 进程之间切换开销较大,一个进程包含1~n个线 程,进程是资源分配的最小单位
线程:同一类的线程共享代码和数据空间, 线程之间切换开销较小,线程是cpu调度的最小单位
线程和进程一样都分为5个阶段: 创建 就绪 运行 阻塞 终止
多进程: 操作系统能同时运行多个任务(程序)
多线程: 同一个程序中有多个顺序流执行(多任务执行就是多线程),提高效率
单线程:单任务,单个路径执行,需要排序,效率较低
学习多线程的核心掌握知识: 线程的开启方式 线程状态 线程安全
线程的创建方式:
1.继承Thread类,重写run() start()
2.实现Runnable接口,重写run() —推荐
3.实现Callable接口,重写call()
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for(int i=1;i<=20;i++) {
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for(int i=1;i<=20;i++) {
//主线程
public static void main(String[] args) {
//创建一个线程
ThreadDemo01 th = new ThreadDemo01();
//开启线程
th.start(); //等待cpu的调度,时间片同一时刻分配给哪一个线程,哪一个线程就会执行,我们无法控制cpu的调度分配
for(int i=1;i<=20;i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("一边敲代码...");
}
}
}
开启线程的第二种方式
1.实现Runnable接口,重写run方法
优点:
1.避免单继承的局限性
2.实现资源共享
public class ThreadDemo02 implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一边吃饭...");
}
}
public static void main(String[] args) {
//1创建线程
Thread th = new Thread(new ThreadDemo02());
//2.开启线程
th.start();
for(int i=1;i<=20;i++) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一边讲课...");
}
}
}
Web12306
实现Runnable接口,实现资源共享案例(当一份资源多个代理同时操作这是就并发,需要控制安全)
例子:
// 有100张票,3个人分别把这100张票买完
public class Web12306_03 implements Runnable{
//共享资源 100张票
int tickets = 100;
public static void main(String[] args) {
Web12306_03 web = new Web12306_03();
//创建线程
Thread th1= new Thread(web,"张三");
Thread th2= new Thread(web,"李四");
Thread th3= new Thread(web,"王五");
//开启线程
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
// A B C
while(true) {
if(tickets<=0) {
break;
}
//A B C
try {
Thread.sleep(100); //ms数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
}
}
}
//龟兔赛跑
兔子,每跑10步就休息5ms
乌龟一直跑
谁先跑完100步,谁就赢了,如果一旦有人赢了,比赛就结束
public class Racer04 implements Runnable{
private String winner;//胜利者
@Override
public void run(){
for(int steps =1;steps<=100;steps++) {
if(Thread.currentThread().getName().equals("rabbit") && steps%10==0) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-->"+steps);
//比赛是否结束
boolean flag = gameOver(steps);
if(flag) {
break;
}
}
}
/**
*
* @param steps 当前参赛者的步数
* @return 是否结束游戏
* true 结束
* false 继续
*/
private boolean gameOver(int steps) {
if(winner!=null) { //存在胜利者
return true;
}else {
if(steps ==100) {
winner = Thread.currentThread().getName();
System.out.println("winner==>"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Racer04 racer = new Racer04();
new Thread(racer,"tortoise").start();
new Thread(racer,"rabbit").start();
}
}
Callable接口
优点:
call方法可以抛出异常
带出返回值
缺点:
使用麻烦
例子:
//模拟龟兔赛跑,实现Callable接口
public class Racer05 implements Callable<Integer>{
private String winner;//胜利者
private boolean gameOver(int steps) {
if(winner!=null) { //存在胜利者
return true;
}else {
if(steps ==100) {
winner = Thread.currentThread().getName();
System.out.println("winner==>"+winner);
return true;
}
}
return false;
}
@Override
public Integer call() throws Exception {
for(int steps =1;steps<=100;steps++) {
//模拟休息 因为兔子总是睡懒觉,如果是兔子的话,我们就让他每走十步睡2毫秒
if(Thread.currentThread().getName().equals("pool-1-thread-1") && steps%10==0) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-->"+steps);
//比赛是否结束
boolean flag = gameOver(steps);
if(flag) {
return steps;
}
}
return null;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Racer05 racer = new Racer05();
//创建执行服务:
ExecutorService ser=Executors.newFixedThreadPool(2);
//提交执行:
Future<Integer> result1 =ser.submit(racer) ;
Future<Integer> result2 =ser.submit(racer) ;
//获取结果:
Integer r1 =result1.get();
Integer r2 =result2.get();
System.out.println(r1+"-->"+r2);
//关闭服务:
ser.shutdownNow();
}
}
通过内部类实现创建多线程
public class ThreadDemo06 {
//静态内部类
static class Inner01 implements Runnable{
@Override
public void run() {
for(int i = 0;i<20;i++) {
System.out.println("一遍听课..");
}
}
}
public static void main(String[] args) {
//局部内部类
class Inner02 implements Runnable{
@Override
public void run() {
for(int i = 0;i<20;i++) {
System.out.println("一遍听歌..");
}
}
}
new Thread(new Inner01()).start();
new Thread(new Inner02()).start();
//匿名内部类
new Thread(new Runnable(){
@Override
public void run() {
for(int i = 0;i<20;i++) {
System.out.println("一遍吃烧烤..");
}
}
}).start();
//Lambda表达式
new Thread(()->{
for(int i = 0;i<20;i++) {
System.out.println("一遍喝啤酒..");
}
}).start();
}
}
线程状态:
新生状态 : new,一个线程一旦进入新生状态,就会有自己的内存和工作空间
就绪状态 : start()方法,线程进入就绪状态,进入就绪不代表会马上执行,会进入就绪队列,等待cpu的调度
运行状态 : 当系统选中某一个正在等待线程,一旦得到获取cpu调度资源,才会运行
阻塞状态 : sleep…
死亡状态|终止状态 : 线程结束
注意:
如果一个线程一旦进入死亡状态,不会恢复,重新创建是一个新的线程了
如果一个线程一旦进入阻塞状态,不会马上恢复运行状态,会先进入就绪,等待cpu的调度
一个线程如何进入终止状态:
1.正常执行完毕
2.stop,destory…(废弃)
3.添加外部标识控制–推荐
如何进入就绪状态:
1).start()
2)阻塞解除
3)调用yield
4)线程切换
如何进入阻塞状态:
1)sleep
2)wait
3)join
sleep: 线程进入睡眠状态,参数为ms数,让出cpu的资源
1.放大问题的可能性
2.模拟网络延迟
//模拟网络延迟倒计时: 倒计时10…9…8…7…1结束
public class StateDemo01 implements Runnable{
public static void main(String[] args) {
new Thread(new StateDemo01()).start();
}
@Override
public void run() {
for(int i=10;i>0;i--) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
join 线程插队
public class JoinDemo03 {
public static void main(String[] args) {
new Thread(new Father()).start();
}
}
class Father implements Runnable{
@Override
public void run() {
System.out.println("想吸烟了..");
System.out.println("给儿子钱,让儿子去买烟..");
//开启儿子线程
Thread t = new Thread(new Son());
t.start();
// try {
// t.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// System.out.println("儿子丢了,去找儿子..");
// }
System.out.println("接过烟吸一口...");
}
}
class Son implements Runnable{
@Override
public void run() {
System.out.println("借过钱...去买烟");
System.out.println("路过一家游戏厅,进去玩耍10s");
for(int i=1;i<=10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+"s");
}
System.out.println("赶紧去买烟...");
System.out.println("烟买好了给老爸..");
}
}
Thread.State getState() 获取线程状态
public class ThreadState02 {
public static void main(String[] args) {
Thread th = new Thread(()->{
for(int i =1;i<=10;i++) {
if(i==5) {
System.out.println("线程即将休眠");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i);
}
});
System.out.println("th线程状态:"+th.getState()); //NEW
th.start();
System.out.println("th线程状态:"+th.getState()); //RUNNABLE
while(th.getState()!=Thread.State.TERMINATED) {
System.out.println("th线程状态:"+th.getState());
}
System.out.println("th线程状态:"+th.getState());
}
}
yield
礼让线程,当线程执行到yield,会让出cpu的资源,直接进入就绪状态
public class YieldDemo02 implements Runnable{
public static void main(String[] args) {
YieldDemo02 y = new YieldDemo02();
new Thread(y,“A”).start();
new Thread(y,“B”).start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+“开始了”);
Thread.yield();
System.out.println(Thread.currentThread().getName()+“结束了”);
}
}
线程优先级
1~10 默认5
void setPriority(int newPriority)
更改线程的优先级。
int getPriority()
返回线程的优先级
MAX_PRIORITY, MIN_PRIORITY,NORM_PRIORITY
注意: 设置优先级,可以提高优先执行的可能,但不一定
public class PriorityDemo04 implements Runnable{
public static void main(String[] args) {
PriorityDemo04 p = new PriorityDemo04();
Thread th1 = new Thread(p,"zhangsan");
Thread th2 = new Thread(p,"lisi");
Thread th3 = new Thread(p,"wangwu");
Thread th4 = new Thread(p,"zhaoliu");
th1.setPriority(1);
th2.setPriority(Thread.MAX_PRIORITY);
th3.setPriority(Thread.NORM_PRIORITY);
th4.setPriority(7);
th1.start();
th2.start();
th3.start();
th4.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
}
}
锁
当多线程的环境下,多个线程操作同一份资源的时候,可能会发生线程不安全问题
控制线程安全:
synchronized 同步 关键字 锁lock 在同步范围内的代码都需要排队执行
同步方法:在方法上使用synchronized关键字进行修饰
静态方法 -->类
成员方法 -->对象
同步块 :
synchronized(类.class|this|资源){
锁住的代码范围
}
类.class : 每一个类否只有一个class对象,可以通过类名.class获取,唯一的
this : 当前对象
资源 : 成员属性
必须为自定义引用数据类型
锁一定要锁不变的内容,才能锁住
锁的范围大,效率低,锁的范围太小,锁不住
思考: 哪一些代码想要线程排队执行,可以把这些代码控制在同步范围内?锁什么能把这些代码锁住?
public class SynWeb12306_01 implements Runnable{
//共享资源 100张票
int tickets = 100;
public static void main(String[] args) {
SynWeb12306_01 web = new SynWeb12306_01();
//创建线程
Thread th1= new Thread(web,"孙悟空");
Thread th2= new Thread(web,"猪八戒");
Thread th3= new Thread(web,"沙悟净");
//开启线程
th1.start();
th2.start();
th3.start();
}
//锁范围内的代码都需要排队执行
// A B C
@Override
public void run() {
while(true) {
// A B C
if(!test()) {
break;
}
}
}
/**
* @return false: 票没了
* return true: 代表还有票
*/
public synchronized boolean test() {
if(tickets<=0) {
return false;
}
//A B C
try {
Thread.sleep(100); //ms数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
return true;
}
}
同步块:
锁类 类名.calss
锁类,这个类的所有对象都被锁住了
可以使用锁this,之锁住当前对象
sleep: 抱着资源睡觉
不会释放对象的锁,会释放cpu的资源
public class SynWeb12306_02 implements Runnable{
//共享资源 100张票
int tickets = 100;
public static void main(String[] args) {
SynWeb12306_02 web = new SynWeb12306_02();
//创建线程
Thread th1= new Thread(web,"孙悟空");
Thread th2= new Thread(web,"猪八戒");
Thread th3= new Thread(web,"沙悟净");
//开启线程
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
while(true) {
//A B C
synchronized (SynWeb12306_02.class) {
if(tickets<=0) {
break;
}
//A B C
try {
Thread.sleep(100); //ms数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
}
}
}
}
生产者消费者模式
通过wait和notify方法实现线程通信,必须使用在一个同步唤醒下
wait 当一个线程进执行到wait方法,进入到一个和该对象相关的等待池中进行等待,等待对方唤醒
同时释放对象锁,和cpu的资源
notidy: 唤醒对象等待池中的线程,被唤醒的线程只是被激活了,需要再次获取对象的资源才能执行
wait,notify,notifyAll方法用于协调多线程对共享数据的处理,所以只能在一个同步环境下使用
sleep: 不会释放对象资源,释放cpu资源
wait: 会释放对象资源,释放cpu资源
public class Demo004Synchronized4 {
public static void main(String[] args) {
SyncStack stack = new SyncStack();
ShengChan sc = new ShengChan(stack);
Chi chi = new Chi(stack);
sc.start();
chi.start();
}
}
// 馒头类
class Mantou {
}
// 有生产、有消费功能的工厂
class SyncStack{
List<Mantou> list = new ArrayList<Mantou>(); // 容器,10就满了
// 生产馒头的方法
public synchronized void push(Mantou mantou){
// 我认为现在已经放满了
if(list.size()==10){
try {
// 通知别人可以来买了
this.notify();//如果不唤醒的话。以后这两个线程都会进入等待线程,没有人唤醒。
// 满了之后就停止生产
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
list.add(mantou);
System.out.println("生产第" + +list.size()+ "个馒头");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 吃馒头的方法
public synchronized void pop(){
// 吃完了
if(list.size()==0){
try {
// 通知别人该生产了
this.notify();; //唤醒在当前对象等待池中等待的第一个线程。notifyAll叫醒所有在当前对象等待池中等待的所有线程。
// 停下吃的动作
this.wait();
//wait后,线程会将持有的锁释放。sleep是即使睡着也持有互斥锁。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.remove(list.size()-1);
System.out.println("现在还剩下 " + list.size() + " 个馒头");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 生产馒头的线程
class ShengChan extends Thread{
SyncStack stack;
public ShengChan(SyncStack stack){
this.stack = stack;
}
@Override
public void run() {
// 一直生产馒头
while(true){
Mantou mantou = new Mantou();
stack.push(mantou);
}
}
}
class Chi extends Thread{
SyncStack stack;
public Chi(SyncStack stack){
this.stack = stack;
}
@Override
public void run() {
// 一直吃馒头
while(true){
this.stack.pop();
}
}
}
例子
//人车公用街道
一个街道 ,有一个信号灯, 提供2个功能 东西走向的功能we 一个南北走向的功能ns
人 走东西 true
车 走南北 false
public class StreetDemo05 {
public static void main(String[] args) {
Street s = new Street();
new Thread(new Person(s)).start();
new Thread(new Car(s)).start();
}
}
class Street{
boolean flag = false;
//南北
synchronized void ns() {
if(flag==true) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("车走....");
//唤醒对方
this.notify();
//车走完了,信号灯要改为true
flag=true;
}
}
//东西
synchronized void we() {
if(flag==false) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("人走....");
//唤醒对方
this.notify();
//车走完了,信号灯要改为true
flag=false;
}
}
}
class Person implements Runnable{
Street street;
public Person(Street street) {
this.street = street;
}
@Override
public void run() {
while(true) {
street.we();
}
}
}
class Car implements Runnable{
Street street;
public Car(Street street) {
this.street = street;
}
@Override
public void run() {
while(true) {
street.ns();
}
}
}