Java学习-09-韩顺平老师
Java-线程入门01
目录:
01-线程的生命周期
02-Synchronized同步
03-互斥锁
04-死锁
05-释放锁
线程的生命周期
线程状态转换图:
线程的七大状态:
1.NEW:尚未启动的线程处于此状态
2.RUNNABLE:在虚拟机上执行处于此状态。
3.BLOCKED:被堵塞等待监视器锁定的线程处于此状态。
4.WAITING:正在等待另一个线程执行特定动作的线程处于此状态。
5.TIMR_WAITING:正在等待另一个线程执行动作达到指定时间的线程处于此状态。
6.TERMINATED:已退出的线程处于此状态。
7.RUNNABLE状态实际上再JVM底层是被分为Ready(在运行状态,但是在等待运行)和RUNNING(正在运行状态)
Synchronized同步
线程同步机制:
1.在多线程编程,一些敏感的数据不允许被多个线程同时访问,此时就是用
同步访问技术,保证数据在任何每一时刻,最多可以由一个线程来方法,
以保证数据的完整性。
2.也可以这样理解:线程同步,即当一个线程在对内存操作的时候,其他线程
都不可以对这个内存进行操作,直到线程操作完,其他线程才能操作该内存。
两种实现方法:
1.同步代码块
synchronized(对象){ 得到对象的锁,才能操作代同步代码
需要同步的代码块
}
2.synchronized还可以放在方法声明中,表示这个方法是同步方法。
public synchronized void m(String name) {
// 需要同步代码块
}
Synchronized解决超出售票问题
public class SellTicket {
public static void main(String[] args) {
WindowSell windowSell = new WindowSell();
// 创建三个线程同时开始操作总共的票数
new Thread(windowSell).start();
new Thread(windowSell).start();
new Thread(windowSell).start();
}
}
@SuppressWarnings({"all"})
class WindowSell implements Runnable{
public static int tickets = 100; // 定义静态属性,所有的窗口对象共享
public static Object obj = new Object();
@Override
public void run() {
while (true){
// 互斥锁是本身 采用代码块形式实现同步
// 当然也可以在上面定义一个对象,例如上面定义的Object
// 则可以写出 synchronized (obj){
// 无论换成哪一个对象,对象必须是所有WinddowSell实例化对象所共有的
// 才可以实现互斥锁。
synchronized (this){
if(tickets == 0){
System.out.println("没有票了");
break;
}
tickets--;
System.out.println("窗口"+ Thread.currentThread() + "售出一张票,剩余" + tickets);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
互斥锁
基本介绍:
1.在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都有对应于一个可称为"互斥锁"的标记,每个标记用来保证任一
时刻,只能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁练习,当某个对象用synchronized
修饰时,表明该对象在任意时刻是能由一个线程来访问。
4.同步的局限性:导致程序的执行效率降低。
5.同步方法(非静态)的锁可以时this,也可以是其他对象(要求是同一个对象)。
6.同步方法(静态的)的锁为当前类本身。
注意细节:
1.同步方法没有使用static修饰:默认锁对象是this
2.如果用方法使用修饰,默认锁对象:当前类本身.class
3.具体事项步骤:
1.需求分析上锁的代码。
2.选择同步代码块或同步方法。
3.要求多个线程的锁对象是同一个。
演示代码可以参考synchronized演示代码
死锁
基本介绍:
多线程都占用对方的锁资源,但不肯想让,导致死锁,在编程过程中要避免死锁的发生。
演示死锁:
public class Deadlock {
public static void main(String[] args) {
new AA(true).start(); // 线程1
new AA(false).start(); // 线程2
}
}
class AA extends Thread {
public static Object obj1 = new Object();
public static Object obj2 = new Object();
public boolean flag;
public AA(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (this.flag) {
// 线程1 进入
synchronized (obj1) {
// 线程1获得了对象obj1 的锁进入1
System.out.println(Thread.currentThread() + "进入了1");
// 线程1需要获取obj2的锁,但是这个锁,被线程2进入else语句占用了
// 所以无法既然进入2
synchronized (obj2) {
System.out.println(Thread.currentThread() + "进入了2");
}
}
} else {
// 线程2 进入
synchronized (obj2) {
// 获得对象obj2的锁,进入3
System.out.println(Thread.currentThread() + "进入了3");
// 线程2需要获取obj1的锁,但是这个锁,被线程2进入else语句占用了
// 所以无法既然进入4
synchronized (obj1) {
System.out.println(Thread.currentThread() + "进入了4");
}
}
}
}
}
// 最终两人人都不能获得对方占用的锁,处于死锁状态,导致程序不会结束
释放锁
以下操作会释放锁:
1.当线程的同步方法、同步代码块执行完毕后,会释放锁。
2.当线程在同步代方法、同步代码块中遇到break或者return时,会释放锁。
3.当线程同步方法、同步代码块中出现未处理的Error或Exception,导致异常结束。
4.当线程在同步代码块、同步代码块中遇到wait()方法的时候,当前线程暂停,就会
释放锁,让其他线程来操作。
可以查看 线程状态转换图 对照
以下操作不会释放锁:
1.线程执行同步代码块、同步方法时,程序调用Thread.sleep(),Thread.yield方法
暂停当前线程时执行,不会释放锁。
2.线程执行同步方法、同步代码块时,其他线程调用该线程的suspend方法,将线程挂起,
该线程不会释放锁。
可以查看 线程状态转换图 对照