学习目标
1:线程同步
2:线程状态(waiting)
学习内容
一:线程同步
1.1、同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchonie(任意对象){
多条语句操作共享数据的代码
}
synchronie(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁。
同步的好处和弊端:
好处: 解决了多线程的数据安全问题。
弊端: 当线程很多时,因为每个线程都会去判断同步上的锁。这是很耗费资源的。无形中会降低程序的运行效率。
1.2、同步方法
同步方法: 就是把synchronined关键字加到方法上
格式
修饰符sychonred 返四值类型 方法名(方法参数){ }
同步方法的锁对象是什么呢?
this
同步静态方法 :就是synchronized以关键字加到静态方法上
格式:
修饰符static synchronized返四值类型 方法名(方法参数){}
同步静态方法的锁是什么呢?
类名. class
例如:
package Demo01;
public class Demo01ticket {
public static void main(String[] args) {
RunnableImpl run = new RunnableImpl();
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
package Demo01;
/*
* 解决线程安全的第二种方法:使用同步方法
* 使用步骤:
* 1、把访问了共享数据的代码块抽取出来,方底一个
* 2、在方法上添加synchronized 修饰符
*/
public class RunnableImpl implements Runnable {
private int ticket = 100;
//Object obj = new Object();
@Override
public void run() {
while(true) {
payTicket();
}
}
public synchronized void payTicket() {
if(ticket>0) {
try {
Thread.sleep(1);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在买第"+ticket+"张票");
ticket--;
}
}
}
结果为:
Lock锁
Lock锁,可以得到和 synchronized一样的效果,即实现原子性、有序性和可见性。
相较于synchronized,Lock锁可手动获取锁和释放锁、可中断的获取锁、超时获取锁。
Lock 是一个接口,两个直接实现类:ReentrantLock(重入锁), ReentrantReadWriteLock(读写锁)。
Lock锁,使用时手动获取锁和释放锁,比synchronized更加灵活;可中断的获取锁;超时获取锁。
Lock 锁的基本用法, l.lock()方法进行上锁, l.unlock()方法进行解锁
例如:
package Demo02;
import Demo01.RunnableImpl;
public class Demo02Ticket {
public static void main(String[] args) {
RunnableImpl run=new RunnableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
package Demo02;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl implements Runnable{
private int ticket=100;
Lock l=new ReentrantLock();
@Override
public void run() {
// TODO 自动生成的方法存根
while(true) {
l.lock();
if(ticket>0) {
try {
Thread.sleep(ticket);
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"票");
ticket--;
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
l.unlock();
}
}
}
}}
结果为:
二:线程状态(waiting)
六大主要线程状态
waiting线程状态图
无限等待状态的前提
该线程必须获取了锁对象
调用线程的wait方法,那么锁对象的持有者就会无限等待了
同时该线程会释放锁,别的线程就有机会获取锁对象了
那么该线程得到锁对象后,就可以调用锁对象的notify/notifyAll方法,唤醒刚刚进入无限等待的线程对象
需要注意的是:被唤醒的线程,并不会进入Runnable,因为没有锁对象,只有其他线程释放了锁对象并且被唤醒的线程获取到了锁对象该线程才会真正的进去Runnable状态
无限等待真的是无限的吗?作为初学者,一定不能被表面的单词所迷惑!!! 其实这里的无限等待是指线程不会自动"醒来"(不像sleep那样,时间到了就会自动"醒来"), 但是别的线程可以"唤醒"它 上面我们提到,处于Waiting状态的线程会释放锁对象,那么其他线程就有机会获取锁对象
另:
其实waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系, 多个线程会争取锁,同时相互之间又存在协作关系。就好比在公司里你和你的同事们,你们可能存在晋升时的竞 争,但更多时候你们更多是一起合作以完成某些任务。 当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入 了Waiting(无限等待)状态,同时失去了同步锁。假如这个时候B线程获取到了同步锁,在运行状态中调用了 notify()方法,那么就会将无限等待的A线程唤醒。注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入 Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)
例如:
package Demo03;
/*
* 等待唤醒案例:
* 创建一个顾客线程:告知老板包子的种类,用wait()方法等待,放弃CPU执行。
* 创建一个老板线程:花5秒做包子,做好后用notif 通知顾客拿包子
*/
public class Demo01WaitAndNotify {
public static void main(String[] args) {
Object obj = new Object();
//创建第一个消费者
new Thread() {
@Override
public void run() {
while(true) {
synchronized (obj) {
System.out.println("消费者1:告知老板包子的种类");
try {
//等待呼叫
obj.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子好了!可以吃了");
System.out.println("=====================");
}
}
}
}.start();
new Thread() {
@Override
public void run() {
while(true) {
synchronized (obj) {
System.out.println("消费者2:告知老板包子的种类");
try {
//等待呼叫
obj.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者2:包子好了!来吃");
System.out.println("=====================");
}
}
}
}.start();
new Thread() {
@Override
public void run() {
//一直做包子
while(true) {
try {
Thread.sleep(3);
}catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println("生产者:老板花2秒做好了包子,呼叫顾客");
//obj.notify();//随机通知一个
obj.notifyAll();//通知所有的人
}
}
}
}.start();
}
}
结果为:
2020080605041