Java SE day 23 多线程(二)

多线程二

1. 线程的常用方法

Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行

Thread.currentThread():表示获取当前正在执行的线程对象

public finalboolean isAlive():测试线程是否处于活动状态

getName():获取线程名字

优先级:可以给线程设置优先级,范围是1-10,10优先级最高

setPriority(数字):给线程设置优先级,这里优先级高,并不代表一定先执行,只是增加了抢占到资源的机会

getPriority():获取线程的优先级,

Thread提供了三个优先级常量:

Thread.MAX_PRIORITY=10

NORM_PRIORITY = 5

MIN_PRIORITY = 1

Main方法的优先级,是5

yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行.

join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程

yield()方法使用如下,

先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,A线程礼让,此时A和B都就绪,然后各自抢占资源,谁抢到资源谁执行,如下:

A线程代码如下:

package cn.sz.gl.test02;
​
public class MyRunA implements Runnable {
​
    @Override
    public void run() {
        MyRunB b = new MyRunB();
        Thread tb = new Thread(b,"B");
        tb.start();
        System.out.println("线程A启动..");
        Thread.currentThread().yield();
​
        System.out.println("线程A结束..");
    }
​
}

B线程如下:

package cn.sz.gl.test02;
​
public class MyRunB implements Runnable {
​
    @Override
    public void run() {
        System.out.println("线程B启动..");
        System.out.println("线程B结束...");
    }
​
}

测试方法如下:

package cn.sz.gl.test02;
​
public class Test {
​
    public static void main(String[] args) {
        MyRunA a = new MyRunA();
        Thread ta = new Thread(a,"A");
        ta.start();
    }
}

join()方法使用如下:

先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,让B线程调用join方法,此时A线程阻塞,B 线程启动执行,当B线程执行完毕后,A线程再继续执行

A线程代码如下:

package cn.sz.gl.test02;
​
public class MyRunA implements Runnable {
​
    @Override
    public void run() {
        MyRunB b = new MyRunB();
        Thread tb = new Thread(b);
        tb.start();
        System.out.println("线程A启动..");
        try {
            tb.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
        System.out.println("线程A结束..");
    }
​
}

B 线程代码如下:

package cn.sz.gl.test02;
​
public class MyRunB implements Runnable {
​
    @Override
    public void run() {
        System.out.println("线程B启动..");
        System.out.println("线程B结束...");
    }
​
}

测试代码如下:

package cn.sz.gl.test02;
​
public class Test {
​
    public static void main(String[] args) {
        MyRunA a = new MyRunA();
        Thread ta = new Thread(a,"A");
        ta.start();
    }
}

2. 线程的终止

第一种,通过给定属性,然后通过控制属性值来破坏继续执行的条件

线程代码如下:

package cn.sz.gl.test03;
​
public class MyRun implements Runnable {
​
    private boolean flag = true;
​
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("执行线程_" + i);
            i++;
        }
    }
​
    public void changeFlag() {
        flag = false;
    }
}

测试代码如下:

package cn.sz.gl.test03;
​
public class Test {
​
    public static void main(String[] args) {
        MyRun mr = new MyRun();
        new Thread(mr).start();
​
​
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
        mr.changeFlag();
    }
}

第二种,对于正在执行的线程,interrupt()是给线程设置中断标志;interrupted()是检测中断并清除中断状态;isInterrupted()只检测中断。还有重要的⼀点就是interrupted()作⽤于当前线程,interrupt()和isInterrupted()作⽤于此线程,即代码中调⽤此⽅法的线程

线程代码如下:

package cn.sz.gl.test03;

public class MyRun2 implements Runnable {

	@Override
	public void run() {
		int i = 0;

		while(!Thread.interrupted()){
			System.out.println("我的线程_"+i);
			i++;
		}

	}

}

测试代码如下:

package cn.sz.gl.test03;

public class Test2 {

	public static void main(String[] args) {
		//System.out.println(Thread.interrupted());//默认为false

		MyRun2 mr = new MyRun2();
		Thread th = new Thread(mr);
		th.start();


		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		th.interrupt();

	}
}

第三种,如果线程因为执行join(),sleep()或是wait()而进入了阻塞状态,此时要想停止它,可以让它调用interrupt(),程序会抛出InterruptedException异常而被catch()子句捕获,进行处理

线程代码如下:

package cn.sz.gl.test03;

public class MyRun3 implements Runnable {

	@Override
	public void run() {
		int i = 0;
		while(true){
			System.out.println("线程_"+i);
			i++;
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				break;
			}
		}
	}

}

测试代码如下:

package cn.sz.gl.test03;

public class Test3 {

	public static void main(String[] args) {
		MyRun3 mr = new MyRun3();
		Thread th = new Thread(mr);
		th.start();


		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		//当调用interrupt方法时,可以改变interrupted的值
		//当th线程中有处理InterruptedException异常的时候,如果该线程调用了interrupt方法,那么该线程会抛出InterruptedException异常
		th.interrupt();
	}
}

3. 线程的同步

在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性;

每个对象都对应于一个可称为"互斥锁"的标记,这个标记保证在任一时刻,只能有一个线程访问对象;

用关键字synchronized给对象加互斥锁.

3.1 同步代码块

synchronized放在对象前面限制一段代码的执行,其语法如下:

Object obj = new Object();
...
synchronized(this){//this被加锁,任何其他要锁this的方法和代码块被阻塞.
 需要同步的代码;
}
...
synchronized(obj){//obj被加锁,任何其他要锁obj的代码块被阻塞.
 需要同步的代码;
}

3.2 同步方法

同步非静态方法:synchronized放在方法声明中,表示整个方法为同步方法,锁定this对象,如果有一个线程进入了该方法,其他线程要想使用当前this对象的任何同步方法,都必须等待前一个线程执行完该同步方法之后

如下:

public synchronized void method1(){
 …
}
public synchronized void method2(){
 …
}
public synchronized void method3(){
 …
}

同步static方法: synchronized放在static方法声明中,表示锁定该类的class对象(xxx.class,是Class类型的,是描述一个类的信息的对象)

如果有一个线程进入了该方法,其他线程要想使用当前类中的任何同步静态方法,都必须等待前一个线程执行完该同步方法之后;其他非同步方法及非静态的同步方法的执行不受影响

如下:

public synchronized static void method1(){
 …
}
public synchronized static void method2(){
 …
}

注意:

当前线程调用本同步方法时,其他线程是不允许调用本对象中其他同步方法

当前线程在调用本同步静态方法时,其他线程是不能够调用本类中的其他同步静态方法的,但是可以调用本类中的其他非同步方法或者非静态同步方法

对于同步静态方法,此时锁定的是对应的类型(类名.class),所有属于该类型的对象,都在其锁定范围内,此时可以实现方法的同步调用

4. 线程阻塞

  1. 同步阻塞

  2. 调用sleep(),join()方法的时候,sleep()转为休眠,时间到自动唤醒

  3. wait(),notify()方法配套,转为等待,同时释放资源,通过notify()方法唤醒

  1. yield(),释放资源,与其他线程重新抢占资源,抢到就执行

  2. suspend()和resume(),由jdk1.0提供,jdk1.2之后就被放弃了,它也是让线程暂停,但是它不释放资源,导致死锁出现

5. 线程死锁

死锁产生的必要条件:

1> 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

2> 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

3> 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占用。

4> 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路

在我们的项目中,我们不希望看到死锁的发生,为了避免,以上四个条件,破坏其中任意一个,死锁就结束了

注意:过多的同步会导致死锁

死锁示例如下:

package cn.sz.gl.no5;

public class MyRun implements Runnable {

	private static Object a = new Object();
	private static Object b = new Object();

	private boolean flag = true;

	public void changeFlag(){
		flag = false;
	}

	@Override
	public void run() {
		if(flag){
			synchronized (a) {
				System.out.println(Thread.currentThread().getName()+"锁定了资源a");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (b) {
					System.out.println(Thread.currentThread().getName()+"锁定资源b");
				}
			}
		}else{
			synchronized (b) {
				System.out.println(Thread.currentThread().getName()+"锁定了资源b");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (a) {
					System.out.println(Thread.currentThread().getName()+"锁定资源a");
				}
			}
		}
	}

}

测试如下:

package cn.sz.gl.no5;

public class Test {

	public static void main(String[] args) {
		MyRun mra = new MyRun();
		new Thread(mra,"线程1").start();

		MyRun mrb = new MyRun();
		mrb.changeFlag();
		new Thread(mrb,"线程2").start();

	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在下张仙人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值