多线程(下篇)
多线程
线程状态
状态介绍
-
new
创建进入新生状态
-
就绪状态
使用start方法立刻进入就绪状态,但是不一定会立刻执行
-
运行状态
CPU调度运行时进入此状态
-
阻塞状态
当调用sleep,wait或者同步锁订时。线程进入阻塞状态
-
dead
运行结束
状态图
线程停止
1、线程自动停止
2、利用标识符停止
实例
package com.py.demo01;
public class TestStop implements Runnable {
private boolean flag = true;
int i = 0;
@Override
public void run() {
//标志位
while (flag){
System.out.println("线程"+(i++));
}
}
public void stop(){
this.flag= false;
}
}
class Test{
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 800; i++) {
if (i==200){
testStop.stop();
System.out.println("=======线程结束======");
}
System.out.println("主线程"+i);
}
}
}
线程休眠
特点
sleep(t)t为休眠时间
线程休眠结束后,进入就绪状态
sleep会抛出InterruptedException异常
sleep可以模拟网络延时、倒计时等
每个对象又会有一个锁,sleep不会释放锁
源码
package com.py.demo01;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) {
//倒计时执行
try {
countdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取系统当前时间
Date time = new Date(System.currentTimeMillis());
while (true){
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
try {
Thread.sleep(1000);
//刷新系统时间
time = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//
}
//倒计时
public static void countdown() throws InterruptedException {
int time = 10;
while (time!=0){
System.out.println("倒计时"+ time);
time--;
Thread.sleep(1000);
}
}
}
线程礼让(yield)
礼让不一定成功,具体情况看CPU调度
demo
package com.py.demo01;
public class TestYield {
public static void main(String[] args) {
YeildDemo yeildDemo = new YeildDemo();
new Thread(yeildDemo,"a").start();
new Thread(yeildDemo,"b").start();
}
}
//线程
class YeildDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始====>");
//如果线程是a,此时将线程礼让出来
if (Thread.currentThread().getName()=="a") {
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"线程停止====>");
}
}
线程加入(Join)
当此线程结束后才可以执行其他线程,此时其他线程阻塞(可以理解为插队)
demo:让大佬插主线程的队
package com.py.demo01;
public class TestJoin {
public static void main(String[] args) {
JoinTread thread = new JoinTread();
//线程进入就绪态
Thread join = new Thread(thread);
join.start();
for (int i = 0; i < 300; i++) {
if (i==100){
try {
//线程开始插队
join.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("我是主线程"+ "\t"+ i);
}
}
}
class JoinTread implements Runnable{
@Override
public void run() {
//休眠的目的是让主线程可以跑到此线程的前面去
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 200; i++) {
System.out.println("大佬来了"+"\t"+i);
}
}
}
线程的状态(State)
常量
NEW
尚未启动的线程处于此状态。RUNNABLE
在Java虚拟机中执行的线程处于此状态。BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED
已退出的线程处于此状态。
Demo:使用主线程查看a线程状态
package com.py.demo01;
public class TestState implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestState people = new TestState();
//new状态
Thread thread = new Thread(people,"a");
Thread.State state = thread.getState();
System.out.println(state);
//就绪状态
thread.start();
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED){
//打印当前状态
try {
//0.1秒查看状态一次
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}
System.out.println("/////////////////////////");
}
}
线程优先级(Priority)
java提供线程调度器,当前所有准备状态的线程。线程调度器根据线程的优先级来决定线程的执行顺序。
线程的优先度作为参考,但是实际情况要依据CPU调度进行处理
Demo:优先级程度高的先执行
package com.py.demo2;
public class TestPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
TestPriority testPriority = new TestPriority();
Thread td01 = new Thread(testPriority);
Thread td02 = new Thread(testPriority);
Thread td03 = new Thread(testPriority);
Thread td04 = new Thread(testPriority);
Thread td05 = new Thread(testPriority);
td01.setPriority(9);
td01.start();
td02.setPriority(4);
td02.start();
td03.setPriority(5);
td03.start();
td04.setPriority(7);
td04.start();
td05.setPriority(2);
td05.start();
}
}
守护线程(daemon)
虚拟机会守护用户线程但是不会保证守护者线程完整执行
当用户线程执行结束后,守护线程会被虚拟机停止
Demo:自动结束的守护者线程
package com.py.demo2;
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
People people = new People();
//设置God线程为守护者线程
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();
//启动用户线程
new Thread(people).start();
}
}
//守护者线程
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("God will protecting you");
}
}
}
//用户线程
class People implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(" a wandfull year!!!!");
}
System.out.println("======BYE WORLD======");
}
}
线程池
类似于连接池,在线程池中创建线程,在有线程任务需要执行时,由线程池出线程,在线程任务结束之后将线程归还给线程池。
线程池使用步骤:
1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。
2、创建一个类,重写run() 方法,创建线程任务
3、调用ExecutorService中的submit()方法,提交任务线程
4、结束线程池使用ExecutorService 的shutdown()方法【不建议使用】
package com.py.demo04;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。
* 2、创建一个类,重写run() 方法,创建线程任务
* 3、调用ExecutorService中的submit()方法,提交任务线程
*/
public class DemoThreadpool {
public static void main(String[] args) {
//1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。
ExecutorService executorService = Executors.newFixedThreadPool(2);
// * 2、创建一个类,重写run() 方法,创建线程任务
RunTasker tasker = new RunTasker();
// 3、调用ExecutorService中的submit()方法,提交任务线程
executorService.submit(tasker);
executorService.submit(tasker);
executorService.submit(tasker);
}
}
线程锁
线程同步机制
并发
多个线程执行同一个资源,并发指的是多个任务交替进行,
同步
线程同步实际上就是一种等待机制,多个同时需要访问此对象的线程进入对象的等待池形成队列
知识延展
同步VS异步
同步和异步通常用来形容一次方法调用。同步方法调用开始后,调用者必须等待被调用的方法结束后,调用者后面的代码才能执行。而异步调用,指的是,调用者不用管被调用方法是否完成,都会继续执行后面的代码,当被调用的方法完成后会通知调用者。
并发与并行
并发和并行是十分容易混淆的概念。并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。实际上,如果系统内只有一个CPU,使用多线程时,在真实系统环境下不能并行,只能通过切换时间片的方式交替进行,从而并发执行任务。真正的并行只能出现在拥有多个CPU的系统中。
阻塞和非阻塞
阻塞和非阻塞通常用来形容多线程间的相互影响,比如一个线程占有了临界区资源,那么其他线程需要这个资源就必须进行等待该资源的释放,会导致等待的线程挂起,这种情况就是阻塞,而非阻塞就恰好相反,它强调没有一个线程可以阻塞其他线程,所有的线程都会尝试地往前运行。
临界区
临界区用来表示公共资源或者说是共享数据,可以被多个线程使用。但是每个线程使用时,一旦临界区资源被一个线程占有,那么其他线程必须等待。
并发问题出现
Demo01:经典买票问题
package com.py.demo2;
public class BuyTicket {
public static void main(String[] args) {
Buy station = new Buy();
//三人分别买票
new Thread(station,"张三").start();
new Thread(station,"李四").start();
new Thread(station,"王五").start();
}
}
class Buy implements Runnable{
//票数
private int sum = 10;
boolean flag = true;
@Override
public void run() {
// 停止标志位
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获得票的人
System.out.println(Thread.currentThread().getName()+ "得到了第"+ sum--+"张票");
//如果票没了就停止循环
if (sum<=0){
flag = false;
}
}
}
}
输出截图
Demo02银行取钱问题
package com.py.demo03;
public class DrawingMoney {
public static void main(String[] args) {
//新建银行
Bank bank = new Bank(10000, "账户");
//取款操作01
Drawing lisi = new Drawing(bank,40,"李四");
lisi.start();
//取款操作02
Drawing wangwu = new Drawing(bank, 4000, "王五");
wangwu.start();
}
}
class Bank{
//银行账户
int account ;
//操作人姓名
String name;
public Bank(int account, String name) {
this.account = account;
this.name = name;
}
}
//取钱线程
class Drawing extends Thread {
//银行
private Bank bank;
//要取得的钱数
private int draw;
//取钱的人
private String name;
public Drawing(Bank bank, int draw, String name) {
super(name);
this.bank = bank;
this.draw = draw;
}
@Override
public void run() {
if (bank.account - draw < 0){
System.out.println("账户余额不足,请重新设置取款金额");
return;
}
bank.account = bank.account - draw;
System.out.println("取款人"+Thread.currentThread().getName()+"\t余额"+bank.account);
}
}
输出截图
造成了李四读到了王五提交后的数据
Synchronize
理论点
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
修饰一个代码块
用static可以保证所需要的资源只有一份
package com.py.demo03;
/**
* 同步线程
*/
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int getCount() {
return count;
}
}
class Man{
public static void main(String[] args) {
/*
//案例1、锁定对象SyncThread后,两个线程开始排队,形成互斥
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();*/
//案例2、锁定对象SyncThread后,两个线程互不干扰,但是因为static存在,所以还是使用的一份count
Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();
}
}
案例一
输出:
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
总结:
两个线程开始相互排队去执行run();一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
案例二
输出:
SyncThread1:0
SyncThread2:1
SyncThread2:2
SyncThread1:2
SyncThread1:4
SyncThread2:3
SyncThread1:5
SyncThread2:6
SyncThread2:8
SyncThread1:7
总结
这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。
修饰一个方法
同步方法也会把同步代码锁住,只让一个程序执行。锁的对象是类对象,也就是this。
package com.py.demo03;
/**
* 同步线程
*/
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public synchronized void run() {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int getCount() {
return count;
}
}
class Man{
public static void main(String[] args) {
Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();
}
}
死锁
多个线程同时抱有对方的需要的资源,导致程序阻塞。
package com.py.demo03;
public class TestLock {
public static void main(String[] args) {
Makeup thread01 = new Makeup(1,"李四");
Makeup thread02 = new Makeup(0,"张三");
thread01.start();
thread02.start();
}
}
class Pen{
}
class Peper{
}
class Makeup extends Thread{
//只有一份的笔 和 纸
static Pen pen = new Pen();
static Peper peper = new Peper();
int choise;
String user;
public Makeup(int choise,String user){
this.user = user;
this.choise = choise;
}
@Override
public void run() {
super.run();
try {
use();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void use() throws InterruptedException {
if (choise == 0 ){
//第一个人先进来获得到笔的使用权
synchronized (pen){
System.out.println(this.user + "获得到笔的使用权");
Thread.sleep(1000);
synchronized (peper){
System.out.println(this.user + "获得到纸的使用权");
}
}
}else{
synchronized (peper){
System.out.println(this.user + "获得到纸的使用权");
synchronized (pen){
System.out.println(this.user + "获得到笔的使用权");
}
}
}
}
}
package com.py.demo03;
public class TestLock {
public static void main(String[] args) {
Makeup thread01 = new Makeup(1,"李四");
Makeup thread02 = new Makeup(0,"张三");
thread01.start();
thread02.start();
}
}
class Pen{
}
class Peper{
}
class Makeup extends Thread{
//只有一份的笔 和 纸
static Pen pen = new Pen();
static Peper peper = new Peper();
int choise;
String user;
public Makeup(int choise,String user){
this.user = user;
this.choise = choise;
}
@Override
public void run() {
super.run();
try {
use();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void use() throws InterruptedException {
if (choise == 0 ){
//第一个人先进来获得到笔的使用权
synchronized (pen){
System.out.println(this.user + "获得到笔的使用权");
Thread.sleep(1000);
synchronized (peper){
System.out.println(this.user + "获得到纸的使用权");
}
}
}else{
synchronized (peper){
System.out.println(this.user + "获得到纸的使用权");
synchronized (pen){
System.out.println(this.user + "获得到笔的使用权");
}
}
}
}
}
程序卡死
lock锁
与Synchronize相同,
不同之处,Lock是显锁(手动开启和关闭锁)synchronize是隐式锁,出了作用域自动释放
lock只有代码块锁,synchronized有代码块和方法锁。
lock锁性能更好,并且具有更好的扩展性
上锁与解锁
void lock(); 上锁
void unlock; 解锁
package com.py.demo03;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 同步线程
*/
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
//线程锁对象
Lock lock = new ReentrantLock();
public void run() {
for (int i = 0; i < 5; i++) {
lock.lock();
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + ":" + (count++));
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
public int getCount() {
return count;
}
}
class Man{
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();
}
}
唤醒线程
线程休眠之后,由其他线程唤醒。
带参数的休眠方法:wait(time);时间达到之后自动唤醒
全部唤醒:notifyAll();
唤醒的是第一个进入锁对象 的线程,
休眠的是第一个进入锁对象 的线程,
package com.py.demo03;
/**
* 等待唤醒案例
* 顾客告知老板需要的食品的数量和种类,唤醒老板线程然后自己休息
* 老板开始生产,生产完成后唤醒顾客线程
* 老板和顾客只能一个在执行
* 只有锁对象才能waiter notify
*/
public class DemoWait extends Thread{
public static void main(String[] args) {
//创建唯一锁
Object obj = new Object();
//顾客线程
new Thread(){
@Override
public void run() {
super.run();
synchronized (obj){
//顾客告知老板需要的食品的数量和种类
System.out.println("要吃什么样的,要多少");
try {
//然后自己休息
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("吃完了走人");
}
}
}.start();
//老板线程
new Thread(){
@Override
public void run() {
//老板等待顾客叫完东西
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
super.run();
synchronized (obj){
//老板开始生产,
System.out.println("老板开始制作");
//生产完成后唤醒顾客线程
obj.notify();
}
}
}.start();
}
}
线程通信
理论:
多线程并发执行时,在默认情况下cpu随机切换线程,当我们需要一个的多线程共同完成一件任务时,希望他们可以有顺序的执行,于是需要他们线程之间进行通信。