《疯狂Java讲义(第4版)》-----第16章【多线程】(控制线程、线程同步)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccnuacmhdu/article/details/84943120

控制线程

join线程

等那个线程做完后,当前线程再做!

import java.lang.Thread;

public class MyThread extends Thread{
	
	public MyThread(String name){
		super(name);
	}
	
	public void run(){
		for(int i = 0; i < 4; i++){
			System.out.println(getName()+" "+i);
		}
	}

	public static void main(String[] args) throws Exception{
		new MyThread("新线程").start();
		for(int i = 0; i < 8; i++){
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i == 4){
				MyThread t = new MyThread("被join的线程");
				t.start();
				t.join();
			}
			
		}
	}
}

上面的程序中,i<4的时候,主线程main和“新线程”并发执行,i=4之后,主线程等待“被join的线程”执行完后,再执行。
在这里插入图片描述
一共有三个join方法如下:
在这里插入图片描述

后台线程

有一种线程,在后台运行,为其他线程提供服务,这种线程被称为“后台线程”,或“守护线程”,或“精灵线程”。JVM的垃圾回收线程就是后台线程。

  1. 如果所有的前台线程都死亡,JVM会通知后台线程死亡。
  2. 调用Thread的setDaemon(true)方法可以把线程设置为后台线程,注意必须是调用start方法之前设定。
  3. 主线程默认是前台线程,默认是前台线程的创建的子线程也默认是前台线程,默认是后台线程的创建的子线程默认也是后台线程
import java.lang.Thread;

public class MyThread extends Thread{
	
	public MyThread(String name){
		super(name);
	}
	
	public void run(){
		//这里的i的范围搞大点,为了让前台main线程执行完后,后台线程就自动死亡
		for(int i = 0; i < 1000; i++){
			System.out.println(getName()+" "+i);
		}
	}

	public static void main(String[] args) throws Exception{
		MyThread t = new MyThread("新线程");
		t.setDaemon(true);//设置为后台线程
		t.start();
		for(int i = 0; i < 8; i++){
			System.out.println(Thread.currentThread().getName()+" "+i);
			
		}
	}
}

在这里插入图片描述

线程睡眠:sleep

让线程进入阻塞状态,即使系统中没有其他可执行的线程,处于sleep状态的线程也不会执行。
在这里插入图片描述
下面程序运行,可以观察到,每打印一次就暂停一秒!
在这里插入图片描述

在这里插入图片描述

改变线程优先级

基于优先级抢占式调度策略情况下,自然优先级高被调度到的概率大了!!!展示的程序就是设置优先级有悬殊,可以看到优先级高的线程被调度的次数比较多了。。。展示程序参看《疯狂Java讲义(第4版)》742~743页。

每个线程默认的优先级和创建他的父线程的优先级相同。

改变优先级和获得优先级的方法:
在这里插入图片描述
在这里插入图片描述

优先级的值可以手动设置为1~10,怎么知道这个范围的呢?可以查看这几个值啊,如下下图。但是要注意的是,最好使用这几个静态常量设置优先级,因为这些优先级依赖于操作系统的支持,手动写上优先级,可移植性弱。。
在这里插入图片描述在这里插入图片描述

线程同步

银行取钱例子说明多线程(线程并发)具有安全问题,见《疯狂Java讲义(第4版)》743~745页

同步代码块、方法

用synchronized修饰代码块、方法(不可修饰构造器、成员变量)。

obj是同步监视器,Java允许把任何对象(注意是对象)作为同步监视器,但根据同步监视器的目的—阻止两个线程对同一个共享资源进行并发访问,通常把有可能被并发访问的共享资源(临界区)作为同步监视器。任何时刻只能由一个线程可以获得对同步监视器的锁定,当同步代码执行完后,该线程就会释放对该同步监视器的锁定。

synchronized(obj){

}

同步锁

常用的锁是ReentrantLock(可重入锁)。注意加锁的是对象(注意是对象)。具体实例参看《疯狂Java讲义(第4版)》750~751页

class X{
	private final ReentrantLock lock = new ReentrantLock();
	//...
	public void m(){
		//加锁
		lock.lock();
		try{
			//需要保证线程安全的代码		
		}
		//用finally保证释放锁
		finally{
			lock.unlock();
		}
	}
}

使用Lock与使用同步方法是类似的,只是使用Lock时显式使用Lock对象作为同步锁,而使用同步方法时系统隐式使用当前对象作为同步监视器,,都是“加锁–修改—释放锁”模式,同一时刻只有一个线程进入临界区。

ReentrantLock锁有可重入性,也就是说,一个线程可以对已加锁的ReentrantLock锁再次加锁。

死锁

当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。具体参看《疯狂Java讲义(第4版)》751~752页示例代码。

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/84943120