线程常用方法第一组
1.setName //设置线程名称,使之与参数 name相同
2.getName //返回该线程的名称
3.strat //使该线程开始执行;java虚拟机底层调用该线程的 start0 方法
4.run //调用线程对象 run方法;
5.setpriority //更改线程的优先级
6.getpriority //获取线程的优先级
7.sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8.interrup //中断线程
package hsp.tanke.method;
public class ThreadMethod {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("killer");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
//主线程打印5次 Hi ,然后我就中断 子线程的休眠.
for (int i = 0; i <5 ; i++) {
Thread.sleep(1000);
System.out.println("hi"+i);
}
//
System.out.println(Thread.currentThread().getName() + "的优先级 = " + t.getPriority());
t.interrupt();//中断 t线程的休眠
}
}
class T extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i <100 ; i++) {
System.out.println(Thread.currentThread().getName() + "吃包子"+i);
}
try {
sleep(20*1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +"被interrupt中断了");
}
}
}
}
线程常用方法第二组
1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程的任务
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收机制
package hsp.tanke.method;
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
MyDeamonThread myDeamonThread = new MyDeamonThread();
//如果我们希望当main线程结束后,子线程自动结束
//只需将子线程设为守护线程即可
myDeamonThread.setDaemon(true);
myDeamonThread.start();
for (int i = 0; i <10 ; i++) {//main线程
System.out.println("强还在工作...");
Thread.sleep(1000);
}
}
}
class MyDeamonThread extends Thread{
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);//休眠1000毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("宋和马还在说话...");
}
}
}
线程的生命周期
线程状态
- NEW
尚未启动的线程处于此状态 - RUNNABLE
在Java虚拟机中执行的线程处于此状态,此状态可再分为 Ready 和 Running 状态 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态 - TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态 - TERMINATED
已退出的线程处于此状态
线程状态转换图
package hsp.tanke.method;
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
T4 t4 = new T4();
System.out.println(t4.getName() + "的状态 " + t4.getState());
t4.start();
while (Thread.State.TERMINATED != t4.getState()){
System.out.println(t4.getName() + "的状态 " + t4.getState());
Thread.sleep(500);
}
System.out.println(t4.getName() + "的状态 " + t4.getState());
}
}
class T4 extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i <10; i++) {
System.out.println("hi" + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
Synchronized
线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
2.也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
同步具体方法-Synchronized
1.同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步代码
}
2.synchonized还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void m (String name){
//需要被同步代码
}
互斥锁
1.Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为 “互斥锁” 的标记,这个标记用来保证在任一时刻,只能由一个线程访问该对象。
3.关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态)的锁为当前类本身。
public class SellTicket {
public static void main(String[] args) {
// SelectTicket02 selectTicket1 = new SelectTicket02();
//SelectTicket02 selectTicket2 = new SelectTicket02();
//SelectTicket02 selectTicket3 = new SelectTicket02();
//selectTicket1.start();//开启线程
//selectTicket2.start();//开启线程
//selectTicket3.start();//开启线程
SelectTicket03 selectTicket01 = new SelectTicket03();
Thread t1 = new Thread(selectTicket01);
Thread t2 = new Thread(selectTicket01);
Thread t3 = new Thread(selectTicket01);
t1.start();
t2.start();
t3.start();
}
}
class SelectTicket02 extends Thread {
private int ticketNum = 100;//卖100张票
private boolean loop = true;
//1. public synchronized static void m1(){} 锁是加在 SelectTicket02.class
//2.如果在静态方法中,实现一个同步代码块
/*
synchronized(SelectTicket02.class){
System.out.println("m2")
}
*/
public synchronized static void m1(){
System.out.println("m2")
}
//1. public synchronized void sell(){} 就是一个同步方法
//2.这时锁在 this对象
//3.也可以在代码块上写 synchronized 同步代码块
public synchronized void sell() {//同步方法,在同一时刻, 只能有一个线程来执行sell方法
if (ticketNum <= 0){
System.out.println("售票结束...");
loop = false;
return;
}
//线程休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数" + (--ticketNum));
}
@Override
public void run() {
while (loop){
sell();
}
}
}
class SelectTicket03 implements Runnable {//3.同步代码块 互斥锁还是在this对象
private int ticketNum = 100;
private boolean loop = true;
Object object = new Object();
public void m() {
synchronized(/*this*/object){
if (ticketNum <= 0){
System.out.println("售票结束...");
loop = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数" + (--ticketNum));
}
}
@Override
public void run() {
while (loop){
m();
}
}
}
死锁
-
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
-
案例
妈妈:你先完成作业,才让你玩手机
小明:你先让我玩手机,我才完成作业
package hsp.tanke.DeadLock;
public class DeadLock {
public static void main(String[] args) {
DeadLockDemo A = new DeadLockDemo(true);
DeadLockDemo B = new DeadLockDemo(false);
A.setName("A");
B.setName("B");
A.start();
B.start();
}
}
class DeadLockDemo extends Thread{
static Object o1 = new Object();//保证多线程, 共享一个对象,这里使用static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag){//构造器
this.flag = flag;
}
@Override
public void run() {
//业务逻辑分析
//1.如果 flag 为 T,线程A 就会先得到/持有 o1 对象锁,然后尝试去获取 o2 对象锁
//2.如果线程A 得不到 o2 对象锁,就会Blocked
//3.如果 flag 为 F,线程B 就会先得到/持有 o2 对象锁,然后尝试去获取 o1 对象锁
//4.如果线程B 得不到 o1 对象锁,就会Blocked
if (flag){
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "进入2");
}
}
}else {
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "进入2");
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "进入1");
}
}
}
}
}
释放锁
- 下面操作释放锁
1.当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
2.当前线程在同步代码块,同步方法中遇到break、return。
案例:没有正常的完事,经理叫他修改bug,不得已出来
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来
4.当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去 - 下面操作不会释放锁
1.线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用
观看韩顺平老师讲解线程视频的笔记
地址:https://www.bilibili.com/video/BV1Ay4y1t7hz