Java学习
day23
目录
- 线程同步
- 线程状态
1、线程同步
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--;
}
}
}
运行结果:
1.3、Lock 锁
虽然我们可以理解同步代码块和同步方法的锁对象问题。但是我们并有直接看别在哪里加上了锁,在哪里释放了锁。为了更清晰地表达如何加上了和释放锁。JDK5以后提供了一个新的说对象look。
Lock实现提供比使用Synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
- v0id Lock ( ) :获得锁
- void unlock ( ): 释放锁
LOCk是接口不能直接实倒化,这里采用它的实现类ReentrantLock来实倒化
- Reentrantlocko :创建-个ReentrantLock的实例
- 例二:
运行代码如下:
①
package Demo02;
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 Demo02;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 解决线程安全的第三种方法:使用Lock锁
* 使用步骤:
* 1、void lock() 获取锁
* 2、void unlock() 释放锁
*/
public class RunnableImpl implements Runnable {
private int ticket = 10;
//1.在成员位置创建一个ReentrantLock 对象
Lock l = new ReentrantLock();
@Override
public void run() {
while(true) {
//2.在可能会出现线程安全的地方,调用LOCK接口中的lock方法,
l.lock();
if(ticket>0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"-->正在买第"+ticket+"张票");
ticket--;//循环
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3、在可能出现线程安全问题的代码调用过后,使用unlock 解锁
l.unlock();
}
}
}
}
}
运行结果:
- 例三:
等待唤醒案例: - 创建一个顾客线程:告知老板包子的种类,用wait()方法等待,放弃CPU执行。
- 创建一个老板线程:花5秒做包子,做好后用notif 通知顾客拿包子
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();
}
}
运行结果:
2、线程状态(图)
tip:2020080605006