[Thinking in Java] - No.7 Java多线程入门

最近在学习Java多线程,算是入了一个门,在这里记录一下如何使用Java实现多线程,其背后的深层原理日后再分析。本文主要介绍以下内容:

1. 使用Thread类和Runnable接口实现Java多线程

2. Java多线程关键字和关键方法

2. Java多线程如何正确停止

3. Java多线程实现加锁


一、Java多线程的创建:

我们使用以下两种方法创建

1. 创建Thread类子类,覆盖run()方法,然后创建Thread实例

public class Stage extends Thread {
	
	@Override
	public void run() {	
	// code here
	}
	
	public static void main(String args []){
		Thread sThread = new Stage();
		sThread.start();
	}
}

2. 创建类实现Runnable接口,创建该类的对象,将对象作为target,创建Thread实例对象。该Thread的实例对象才是真正的线程对象

public class ArmyRunnable implements Runnable {

	@Override
	public void run() {	
	// code here
	}
}
ArmyRunnable a= new ArmyRunnable();
		
Thread t = new Thread(a, "线程名称");
t.start();

二、关键字和关键方法:

关键字:

volatile:修饰变量,使得变量具有可见性。即其他的线程也可以使用并修改该变量。

synchronized:它用来修饰一个方法或者代码块的时候,意味着当前只能有一个线程执行该方法或者代码块。需要配合锁变量来使用

private final Object lockObj = new Object();
synchronized(lockObj){
// code here
}
关键方法:

sleep(): Thread.sleep(1000),Thread类的静态函数,让当前线程休眠1000ms,从而让开CPU,但是并不会释放锁

yield(): 通俗的解释,就是当前的线程让出CPU,变成就绪态,然后CPU从所有就绪态的线程中重新选择一个线程运行。该线程仍然可能被选中运行。

run()/start():线程运行具体的业务代码要放到run()函数中,但是我们运行线程,要使用start()函数。

join():A.join()会阻塞A所在的线程,使得A线程完成以后,其所在的线程才会继续运行

wait()/notify()/notifyAll():用于加锁,释放锁,如下所示:

synchronized (lockObj) {
// 此处添加if-else判断语句,如果条件不满足那么进入锁标志等待序列
	try {
		lockObj.wait();
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
	        e.printStackTrace();

	}
	//此处添加if-else判断语句,如果条件满足,那么进行相关操作
	// code here
	// 线程释放锁,同时唤醒锁标志等待池中的所有对象
	lockObj.notifyAll();
}


wait(): 释放锁,并且进入对象等待池

notify():唤醒对象等待池中的一个线程,放入锁标志等待池中

notifyAll():唤醒对象等待池中所有等待某个锁标志的线程,放入对应的锁标志等待池中

三、正确停止Java线程:

错误方法1:使用stop(),使得线程突然停止,甚至破坏线程的原子性。已经被废弃了

错误方法2:使用interrupt ()

public void run(){
		while(!this.isInterrupted()){
			System.out.println("Thread is running..");
			long time = System.currentTimeMillis();
			Thread.sleep(1000);
		}
	}

使用interrupt停止线程
thread.interrupt();

发现,上述的线程并不能被中断,会继续运行,同时抛出异常。

我们查看Java API可以发现:


当我们使用了正常使用interrupt的时候,会设置中断位;但是如果该线程被阻塞了,例如使用了wait(),sleep()的时候,这时,线程的中断位就会被清除,同时会收到InterruptedException 异常。这就可以解释上边的线程为什么不能很好地退出。

正确方法:设置线程标志位

public class ArmyRunnable implements Runnable {

	volatile boolean keepRunning = true;
	@Override
	public void run() {	
		while(keepRunning){
			// ...
		}
	}
}
当我们在外部改变了keepRunning的值的时候,该线程就不会在运行。这样就可以保证我们想要的线程原子性,而不会突然中断

四、线程加锁

在第二部分已经讲的差不多。主要就是定义一个 Object类型的锁变量,使用synchronized关键字标记临界区,然后使用wait和notify函数来标志阻塞或者唤醒线程

private final Object lockObj = new Object();  
	public void func(int from, int to, double amount) {
		synchronized (lockObj) {
			// 此处添加if-else判断语句,如果条件不满足那么进入锁标志等待序列
			try {
				lockObj.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();

			}
			//此处添加if-else判断语句,如果条件满足,那么进行相关操作
			// code here
			// 线程释放锁,同时唤醒锁标志等待池中的所有对象
			lockObj.notifyAll();
		}

	}





P.S.文章不妥之处还望指正


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值