Java_多线程(二)

一、线程间的通信

1. 两个线程间的通信
  1. 什么时候需要通信?
    a. 多个线程并发执行时,在默认情况下CPU是随机切换线程的;
    b. 如果我们希望它们有规律的执行,就可以使用通信,例如每个线程执行一次打印。
  2. 怎么通信?
    a. 如果希望线程等待,就调用wait();
    b. 如果希望唤醒等待的线程,就调用notify();
    c. 这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。
    针对Java_多线程(一)中同步代码块部分的代码,如果我们希望:打印一行程序员,打印一行架构师,打印一行程序员…,我们可以对里面类Printer的代码进行修改:
//等待唤醒机制
class Printer{
	private int flag = 1;
	public void print1() throws InterruptedException{
		synchronized(this){  
			if(flag != 1){
				this.wait();      //当前线程等待
			}
			System.out.print("程");
			System.out.print("序");
			System.out.print("员");
			System.out.print("\r\n");	
			flag = 2;
			this.notify();     //随机唤醒单个等待的线程
		}
	}
	public static void print2() throws InterruptedException{
		synchronized(this){
			if(flag != 2){
				this.wait();    
			}
			System.out.print("架");
			System.out.print("构");
			System.out.print("师");
			System.out.print("\r\n");	
			flag = 1;
			this.notify();
		}
	}
}
2. 三个或三个以上线程间的通信

多个线程通信的问题:
a. notify()方法时随机唤醒一个线程;
b. notifyAll()方法是唤醒所有的线程;
c. jdk5之前无法唤醒指定的一个线程;
d. 如果多个线程之间通信,需要使用 notifyAll()通知所有线程,用while来反复判断条件。

3. 线程间通信需注意的问题
  1. 在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法。
    在这里插入图片描述
  2. 为什么wait方法和notify方法定义在Object这个类中?
    因为锁对象可以是任意对象,Object是所有类的基类,所以wait方法和notify方法需要定义在Object这个类中。
  3. sleep方法和wait方法的区别?
    a. sleep方法必须传入参数【参数就是时间】,时间到了自动醒来;wait方法可以传入参数,也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待。
    b. sleep方法在同步函数或同步代码块中,不释放锁【CPU一直在这里耗着】;wait方法在同步函数或者同步代码块中,释放锁。

二、JDK1.5的新特性–互斥锁

  1. 同步
    使用ReentrantLock类的lock()【获取锁】和unlock()【释放锁】方法进行同步

  2. 通信
    a. 使用ReentrantLock类的newCondition()方法可以获取Condition对象;
    b. 需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法;
    c. 不同的线程使用不同的Condition,这样就能区分唤醒的时候找哪个线程了。

void await() :造成当前线程在接到信号或被中断之前一直处于等待状态
void signal() :唤醒一个等待线程

对于在上面实现的两个线程间的通信,我们可以利用这个新特性实现。代码如下:

//等待唤醒机制
class Printer{
	private ReentrantLock r = new ReentrantLock();
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	
	private int flag = 1;
	public void print1() throws InterruptedException{
		r.lock();  
		if(flag != 1){
			c1.await();      //当前线程等待
		}
		System.out.print("程");
		System.out.print("序");
		System.out.print("员");
		System.out.print("\r\n");	
		flag = 2;
		c2.signal();
		r.unlock();
	}
	public static void print2() throws InterruptedException{
		r.lock();
		if(flag != 2){
			c2.await();    
		}
		System.out.print("架");
		System.out.print("构");
		System.out.print("师");
		System.out.print("\r\n");	
		flag = 1;
		c1.signal();
		r.unlock();
	}
}

三、线程组的概述和使用(了解)

1. 线程组概述
  1. Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
  2. 默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup()    //通过线程对象获取它所属于的组
public final String getName()                //通过线程组对象获取线程组的名字

我们通过实现Runnable接口【MyRunnable (implements Runnable)】的方法创建线程对象,验证:默认情况下,线程属于主线程组。

public static void main(String[] args){
	MyRunnable mr = new MyRunnable();
	Thread t1 = new Thread(mr,"张三");
	Thread t2 = new Thread(mr,"李四");

	ThreadGroup tg1 = t1.getThreadGroup();
	ThreadGroup tg2 = t2.getThreadGroup();

	System.out.println(tg1.getName());
	System.out.println(tg2.getName());
	
}

运行结果如下:得以验证。
在这里插入图片描述

  1. 我们也可以给线程设置分组:
    a. ThreadGroup(String name),创建线程组对象并给其赋值名字;
    b. 创建线程对象;
    c. Thread(ThreadGroup group, Runnable target, String name);
    //分配新的Thread对象,以便将target作为其运行对象,将指定的name作为其名称,并作为group所引用的线程组的一员
    d. 设置整组的优先级或者守护线程。
2. 案例演示

实现自定义线程组。代码实现如下:

public static void main(String[] args){
	ThreadGroup tg = new ThreadGroup("我是一个新的线程");     //创建新的新的线程组
	MyRunnable mr = new MyRunnable();         //创建Runnable的子类对象
	
	Thread t1 = new Thread(tg,mr,"张三");      //将线程t1放入组中
	Thread t2 = new Thread(tg,mr,"李四");      //将线程t2放入组中

	System.out.println(t1.getThreadGroup().getName());    //获取组名
	System.out.println(t2.getThreadGroup().getName());
	
}

运行结果如下:
在这里插入图片描述

四、线程的五种状态【生命周期】

  1. 新建:创建线程对象
  2. 就绪:线程对象已经启动了,但是还没有获取到CPU的执行权
  3. 运行:获取到了CPU的执行权
  4. 阻塞:没有CPU的执行权,回到就绪
  5. 代码执行完毕,线程死亡

在这里插入图片描述

发布了32 篇原创文章 · 获赞 3 · 访问量 1347

猜你喜欢

转载自blog.csdn.net/weixin_44270855/article/details/104404823
今日推荐