线程状态的介绍:
1、新生状态:通过start()方法到达就绪状态
2、就绪状态:获得执行权的时候到达运行状态
3、运行状态:
a.当失去执行权的时候
b.通过synchronized关键字的使用之后产生同步阻塞,表明对象锁或者class锁可用时
c.通过wait()方法达到等待阻塞状态
d.通过调用sleep()/join()方法io流阻塞等,因为其他阻塞回到就绪状态,或者在run()方法结束之后进入了死亡状态。
4、死亡状态
在线程的API文档Thread.state枚举五种状态
1、NEW
尚未启动的线程处于此状态
2、RUNNABLE
在Java虚拟机中执行的线程处于此状态
3、BLOCKED
被阻塞等待监视器锁定的线程处于此状态
4、WAITING
正在等待另一个线程执行特定动作的线程处于此状态
5、TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间线程处于此状态
6、TERMINATED
已退出的线程处于这个状态
线程的优先级:
Java提供一个线程调度器来监控程序中启动进入就绪状态的所有线程。线程调度器按照线程的优先级决定应该调度哪个线程来执行。
线程的优先级用数字来表示,范围1到10
-
Thread.MIN_PRIORITY = 1
-
Thread.MAX_PRIORITY = 10
-
Thread.MIN_PRIORITY = 5
使用下述方法来获得或者设置线程对象的优先级 -
int getPriority()
-
void setPriority(int newPriority);
优先级的设定建议在start()调用前
注意:优先级低知识意味着调度的概率低。
常用的线程方法
1、yield方法
- 礼让线程,让当前正在执行线程暂停
- 不是阻塞线程,而是将线程从运行状态转入就绪状态
- 让cpu调度器重新调度
2、join方法
插队线程
- join合并线程,待此线程执行完成之后,再执行其他线程,其他线程阻塞
注意:这里是调用join方法的线程对象插别人的队
3、sleep()方法
4、wait()方法
5、notify()方法
守护线程:
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如后台记录操作日志、监控内存使用等
守护线程是为用户线程所提供服务的
jvm停止不用等待守护线程执行完毕
常用的其他方法:
1、isAlive():判断线程是否还活着,即线程是否还未终止
2、setName():给线程起一个名字
3、getName():获取线程的名字
4、currentThread():取得当前正在运行的线程的对象,也就是获取自己本身
不同的线程有自己的工作内存,同时工作内存和主内存互相交互。所以就会产生了线程不安全的问题
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这个时候,我们就需要用到“线程同步”。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个线程的**“等待池形成队列”**,等待前面的线程使用完毕后,下一个线程再使用。
为了保证数据在方法中被访问的正确性,在访问时加入锁机制,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用释放锁即可。存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。
若将一个大的方法声明为synchronized关键字 将会大大影响效率。
synchroinzed的没有锁住资源时的代码:
public class Unsafe02 {
public static void main(String[] args) {
//账户
Account account = new Account(100,"结婚礼金");
Drawing you = new Drawing(account,80,"可悲的你");
Drawing wife = new Drawing(account,90,"happy的她");
you.start();
wife.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money,String name)
{
super();
this.money = money;
this.name = name;
}
}
//模拟取款
class Drawing extends Thread{
Account account; //取钱的账户
int drawingMoney; //取的钱数
int pocketTotal; //取的总数
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
/**
* 锁的目标不对这里不是锁this应该锁定account
*/
public void run() {
test();
}
public synchronized void test() {
if(account.money - drawingMoney < 0)
{
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
pocketTotal += drawingMoney;
System.out.println(this.getName() + "-->账户余额为:"+ account.money);
System.out.println(this.getName() + "-->口袋余额为:"+ pocketTotal);
}
}
锁住正确的对象:
public synchronized void test() {
synchronized(account) {
if(account.money - drawingMoney < 0)
{
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
pocketTotal += drawingMoney;
System.out.println(this.getName() + "-->账户余额为:"+ account.money);
System.out.println(this.getName() + "-->口袋余额为:"+ pocketTotal);
}
}
- 同部块:synchronized(obj){ },obj称之为同部监视器
- obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器是this即该对象本身,或者class即类的模子
同部监视器的执行过程:
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器未锁,锁定并访问
静态块先于构造块执行,同步块可以密度更小地去锁定资源
死锁:多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块拥有“两个以上的对象锁”时,会发生“死锁”的问题。
额外小知识科普:
PV:page view页面访问量
uv:unicue view用户访问量
vv: visit view 表示访问量
看一天的访问量就看pv
生产者消费者模式
举例:点餐之后在旁边等餐
应用于线程通信
分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
- 对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又要通知消费者消费
- 对于消费者,消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费
- 在生产者消费者问题中,仅有synchronized时不够的
- synchronized可阻止并发并更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
线程通信有两个解决方法:
1、并发协作模型“生产者/消费者模式”->管程法
用容器实现
- 生产者:负责生产数据的模块(模块可能是:方法、对象、线程、进程)
- 消费者:负责处理数据的模块(模块可能是同上)
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”;生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。
- Buffer(共享内存)让消费者和生产者之间解耦、提高效率
2、信号灯法(红绿灯)
Java提供了三个方法解决线程之间的通信问题:
1、final void wait()
作用:表示线程一直等待,直到其他线程通知与sleep不同,会释放锁
2、final void wait(long timeout)指定等待的毫秒数
3、final void notify()
作用:唤醒一个处于等待状态的线程
4、final void notifyAll()
作用:唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
以上方法均是java.lang.object类的方法,都只能在同步方法或者同步代码中使用,否则会抛出异常。
管程法的代码:
/**
* 协作者模型:生产者消费者实现之一:管程法
* @author 33212
*
*/
public class CoTest01 {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("生产-->"+ i+"个馒头");
container.push(new Steamedbun(i));
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
public void run() {
//消费
for(int i = 0; i < 1000; i++) {
System.out.println("消费-->"+ container.pop().id+"个馒头");
}
}
}
//缓冲区
class SynContainer{
Steamedbun[] buns = new Steamedbun[10];
private int count = 0;
//存储 生产
public synchronized void push(Steamedbun bun) {
//何时能生产 容器存在空间
//不能生产
if(count == buns.length) {
try {
this.wait(); //线程阻塞,消费者通知生产
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//存在空间 可以生产
buns[count] = bun;
count++;
//存在数据了, 可以通知消费了
this.notifyAll();
}
//获取
public synchronized Steamedbun pop() {
//何时能消费 容器中是否存在数据
//没有数据,只有等待
if(count == 0) {
try {
this.wait();//线程阻塞 生产者通知消费解除阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//存在数据可以消费
count--;
Steamedbun bun = buns[count];
this.notifyAll(); //存在空间了 可以唤醒对方生产了
return bun;
}
}
//馒头
class Steamedbun{
int id;
public Steamedbun(int id) {
super();
this.id = id;
}
}
信号灯法例子:
/**
* 协作者模型:生产者消费者实现之一:信号灯
* @author 33212
*
*/
public class CoTest02 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者 演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0; i < 20 ;i++) {
if(i % 2 == 0) {
this.tv.play("奇葩说");
}else {
this.tv.play("太污了,和李白嘻嘻最");
}
}
}
}
//消费者 观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//同一个资源 电视
class TV{
String voice;
//信号灯
//T 表示演员表演 观众等待
//F 表示观众观看 演员等待
boolean flag = true;
//表演
public synchronized void play(String voice) {
//演员等待
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("表演了" + voice);
this.voice = voice;
this.notifyAll();
//切换信号灯
this.flag = !this.flag;
}
public synchronized void watch() {
//观众等待
if(flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("听到了"+ voice);
//唤醒
this.notifyAll();
this.flag = !this.flag;
}
}```