目录
1.线程终止
线程终止即停止执行此线程
线程终止分为两种情况:
一种是等待线程执行完后 它会自动终止
另一种是通过使用变量来控制run方法退出的方式来终止线程 即通知方式
我们来看看第二种方式的具体实现
public class Pre04 {
public static void main(String[] args) throws InterruptedException {
test04 test04 = new test04();
test04.start();
//主线程
for (int i = 0; i <10; i++) {
Thread.sleep(1000);
System.out.println("主线程"+i);
}
test04.setLoop(false);
while(true){
System.out.println("主线程继续执行");
}
}
}
class test04 extends Thread{
boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
int counts = 0;
@Override
public void run() {
while(loop){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"线程"+counts++);
}
}
}
执行效果如下
主线程7
主线程8
Thread-0线程8
Thread-0线程9
主线程9
主线程继续执行
主线程继续执行
在主线程9输出后 后续不再执行Thread-0线程 成功终止了Thread-0线程
2.线程的常用方法
public class Pre04 {
public static void main(String[] args) throws InterruptedException {
test04 test04 = new test04();
//1.给线程起名字
test04.setName("hsp老师讲的太好了");
//2.设置线程优先级
test04.setPriority(Thread.MAX_PRIORITY);//高优先级 低优先级 中等优先级
test04.start();
int counts = 0;
//主线程
for (int i = 0; i <10; i++) {
Thread.sleep(1000);
System.out.println("主线程"+i);
}
//3.打断子线程休眠
test04.interrupt();
//4.yield线程的礼让
// Thread.yield();
}
}
class test04 extends Thread{
int counts = 0;
@Override
public void run() {
try {
Thread.sleep(100000);//100s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"线程"+counts++);
}
}
前三个 起名字 就是给线程重命名 设置线程优先级 优先级高的更容易被执行(概率) 打断休眠就是让子线程跳过休眠状态 然后运行
public class Pre04 {
public static void main(String[] args) throws InterruptedException {
test04 test04 = new test04();
test04.start();
int counts = 0;
//主线程
for (int i = 0; i <10; i++) {
Thread.sleep(1000);
System.out.println("主线程"+i);
if(i==5){
//5.join 线程插队
test04.join();
}
}
//4.yield线程的礼让
Thread.yield();
}
}
class test04 extends Thread {
int counts = 0;
@Override
public void run() {
while (counts < 10) {
try {
Thread.sleep(1000);//100s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程" + counts++);
}
}
}
最后两个 一个是插队 一个是礼让 礼让不一定成功 礼让的含义是如果只有一个厕所 我就让给你 但是事实上可能有多个厕所 所以不一定成功 插队是指 老板来了 必须得让他先上 在程序上体现为插队的线程全部执行完了 再回过头执行原线程
运行效果
Thread-0线程3
Thread-0线程4
主线程4
Thread-0线程5
主线程5//到五主线程停止输出 被插队
Thread-0线程6
Thread-0线程7
Thread-0线程8
Thread-0线程9//Thread-0全部执行完毕 让给主线程
主线程6
主线程7
3.守护线程
1.用户线程:也叫工作线程 以线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的 当所有的用户线程结束后 守护线程自动结束 例如 最典型的就是垃圾回收线程
public class Pre05 {
public static void main(String[] args) throws InterruptedException{
test05 test05 = new test05();
test05.setDaemon(true);//设置为守护线程
test05.start();
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("主线程"+i);
}
}
}
class test05 extends Thread{
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程执行");
}
}
}
执行效果如下
子线程执行
主线程0
子线程执行
主线程1
子线程执行
主线程2
主线程3
子线程执行
子线程执行
主线程4
主线程5
子线程执行
子线程执行
主线程6
子线程执行
主线程7
子线程执行
主线程8
子线程执行
主线程9
当主线程(用户线程)运行结束后 子线程(守护线程)跟着结束
4.线程的生命周期
-
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
- NEW
public class Thread10 {
public static void main(String[] args) throws InterruptedException{
R r = new R();
System.out.println(r.getName()+"状态"+r.getState());
r.start();
while(Thread.State.TERMINATED!=r.getState()){
System.out.println(r.getName()+"状态"+r.getState());
Thread.sleep(1000);
}
System.out.println(r.getName()+"状态"+r.getState());;
}
}
class R extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i < 10; i++) {
System.out.println("hi"+i);
try {
Thread.sleep(1000);//Thread-0状态TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
目前了解就好 后面会深挖 可以跑下下面的程序体会线程的不同状态
5.Synchronized关键字
前面提到过多线程卖票 有可能会超卖 即当只剩最后一张票时 有可能三个线程同时进入 这样会导致超卖问题 为了解决这个问题呢 我们引入了线程同步机制
线程同步机制
1.在多线程编程 一些敏感数据不允许被多个线程同时访问 此时就使用同步访问技术 保证数据在任何时刻 最多有一个线程访问 以保证数据的完整性
2.也可以这样理解:线程同步 即当有一个线程在对内存进行操作时 其他线程都不可以对这个内存地址进行操作 直到该线程完成操作 其他线程才能对该内存地址进行操作 即每次只能有一个线程访问
同步具体方法-Synchronized
两种形式
第一种synchronized同步代码块
public class Pre06 {
public static void main(String[] args) {
test06 test060 = new test06();
test06 test061 = new test06();
test06 test062 = new test06();
test060.start();
test061.start();
test062.start();
}
}
class test06 extends Thread{
private static int num = 100;
@Override
public void run() {
synchronized (this) {
while (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程卖出一张票,还剩" + num-- + '张');
}
}
}
}
卖票问题 此时不会超发
第二种 同步方法
public class Pre06 {
public static void main(String[] args) {
test06 test060 = new test06();
test06 test061 = new test06();
test06 test062 = new test06();
test060.start();
test061.start();
test062.start();
}
}
class test06 extends Thread{
private static int num = 100;
@Override
public void run() {
make();
}
public synchronized void make(){
while (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程卖出一张票,还剩" + num-- + '张');
}
}
}
6.互斥锁
1.在java语言中 引入了对象互斥锁的概念 来保证共享数据操作的完整性
2.每个对象都对应于一个可称为“互斥锁”的标记 这个标记用来保证在任一时刻 只能有一个线程访问该对象
3.关键字synchronized与对象的互斥锁联系 当某个对象被synchronized修饰时 表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率降低
5.同步方法(非静态的) 的锁可以是this 也可以是其他对象
Object object = new Object();//三个线程操作的是同一个对象
@Override
public void run() {
synchronized (object){
//必须是一个对象
6.同步方法(静态的)锁是当前类本身
public synchronized static void kush(){
//锁加在类上
}
public static void m2(){
synchronized (A1.class){//加在类本身上
System.out.println("shjaj");
}
}
7.线程死锁
基本介绍:多个线程都占用了对方的锁资源 但不肯相让 导致了死锁 在编程时一定要避免死锁的发生
比如 一个很生活化的例子
妈妈:你做完作业 我才让你玩手机
小明:我先玩手机 才能写作业 (hhh)
模拟死锁的过程
public class Thread11 {
public static void main(String[] args) {
//模拟死锁
DeadLock deadLock = new DeadLock(true);
DeadLock deadLock1 = new DeadLock(false);
deadLock.start();
deadLock1.start();
}
}
class DeadLock extends Thread{
//保证多线程共用一个对象
//如果flag为T 先拿到o1对象锁 再尝试获取o2对象锁
//如果线程A得不到o2对象锁 就会Blocked
//如果flag为F 线程B就会先拿到o2对象锁 再尝试获取o1对象锁
//如果线程B得不到o1对象锁 就会Blocked
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag;
public DeadLock( boolean flag) {
this.flag = flag;
}
@Override
public void run() {
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()+"进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"进入4");
}
}
}
}}
8.释放锁
1.当前线程的同步方法 同步代码块执行结束
2.当前线程在同步代码块 同步方法中遇到break return
3.当前线程在同步代码块 同步方法中出现了 未处理的Error或者Exception 导致异常结束
4.当前线程在同步代码块 同步方法中执行了线程对象的wait方法 当前线程暂停 并且释放锁
1.线程执行同步代码块或者同步方法时 程序调用Thread.sleep()和yield方法不会释放锁
2.线程执行同步代码块时 其他线程调用了该线程的suspend方法将该线程挂起 该线程不会释放锁 应尽量避免这种情况