Java高并发程序设计学习笔记(二):多线程基础

什么是线程?

线程是进程内的执行单元。
每个进程中有若干个线程,进程的切换是非常重量型的,所线程可以作为较为广泛的并发涉及

java中调动了线程会映射到操作系统中,两者是等价的

线程的基本操作

Java高并发程序设计学习笔记(二):多线程基础

线程的基本操作

新建线程

Thread t1=new Thread(); t1.start();
每一个线程都是runable接口的实现
start方法就能把这个线程跑起来,开启一个线程
在一个新的操作系统的线程上面调用run方法
Thread t1=new Thread(); t1.run(); 
不能开启线程

调用run方法和调用start方法做的事情是一样的 ,但是run并不会开启新的线程而是在调用run的当前的这个线程当中执行这个操作,只有使用的start方法才是在真的一个新的线程当中执行run中的事情

Thread.run()的实现 target 是Runnable接口(run是runnable接口中的一个方法)
run方法源码

private Runnable target;
public void run() {
if (target != null) {
 target.run();
} }

Thread 的init方法:

public Thread(){
	init(null,null,"Thread-"+nextThreadNum(),0)
	//init方法是在new对象的时候自动执行的
}
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

调用run的一种方式

这里的target本身就是传的null,所以就可以把run方法重载,把我们的方法写进去

Thread t1=new Thread(){ 
@Override
public void run(){
System.out.println("Hello, I am t1");
} };
t1.start();

调用run另的一种方式

将target传到init方法中,来运行run方法。如下面CreateThread3(一个runnable的实例),这里不需要重载run方法,t1.start()的时候会自动去调用target.run。

Thread t1=new Thread(new CreateThread3()); t1.start();

终止线程 (弃用)

– Thread.stop() 不推荐使用。它会释放所有monitor
记录1:ID=1,NAME=小明
记录2:ID=2,NAME=小王

Java高并发程序设计学习笔记(二):多线程基础
如上图,在读和写的时候加入锁,当写完id,正要写name的时候,stop掉了thread,这个时候,会把锁释放掉,导致将id写了进去,name没有写进去,出现数据的不一致。

中断线程

public void Thread.interrupt() // 中断线程
public boolean Thread.isInterrupted() // 判断是否被中断
public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态

public void run(){ while(true){
Thread.yield();
} }
t1.interrupt();

t1.interrupt();我只是告诉线程你应该终止了,对线程没有任何的影响,还是在跑着的。

public void run(){ while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!"); 
break;
}
Thread.yield();
} }

通过Thread.currentThread().isInterrupted()判断当前的线程是不是被interrupt(),告知过要中断,是的话就break出while循环,同时终止run方法,也就自动终止了thread,这里不会出现数据的不一致,因为是在Thread.yield();(下一次数据处理之前)对线程进行终止的。
拓展

public static native void sleep(long millis) throws InterruptedException

public void run(){ while(true){
  		if(Thread.currentThread().isInterrupted()){ 
  		System.out.println("Interruted!");
  		break; }
  		try {
  		Thread.sleep(2000);
  		} catch (InterruptedException e) {
  		System.out.println("Interruted When Sleep"); 
  		//设置中断状态,抛出异常后会清除中断标记位 
  		Thread.currentThread().interrupt();
  		}
  		 Thread.yield();
}
} 

sleep即休眠方法。为什么休眠方法需要抛出 throws InterruptedException异常呢?
如果我在休眠的过程中,线程出现了isInterrupted(中断)的请求怎么办?这个时候如果sleep会抛出一个异常,同时自动清除interrupt()设置的标志位,所以需要在抛出的异常中添加Thread.currentThread().interrupt();以保证能够被上面的Thread.currentThread().isInterrupted()检测到从而中断线程。

挂起(suspend)和继续执行(resume)线程 (弃用)

suspend()不会释放锁 ,发现suspend之后现场称还是runnable的状态,当然不代表所有的挂起后线程会runnable。
如果加锁发生在resume()之前 ,则死锁发生Java高并发程序设计学习笔记(二):多线程基础
如上图,线程1挂起可,这个时候没有释放锁,希望通过线程2来resume锁,但是线程2的resume可能意外的发生在suspend之前,这个时候线程1将会永远的挂,其他的锁(如上图线程3)将会无线等待线程1拿到的锁。

等待线程结束(join)和谦让(yeild)

yeild

当前线程优先级不是特别高,希望其他线程有机会争夺cpu时间,所以讲当前占用的cpu事件释放掉,使得其他的线程有更多的机会继续往下执行,但是下次还是有机会拿到cpu时间,不代表永远的让出去。

join

线程a希望知道线程b什么时候结束,因为需要在线程b结束的时候来立马做某些事情。

public class JoinMain {
public volatile static int i=0;
public static class AddThread extends Thread{
@Override public void run() {
for(i=0;i<10000000;i++); }
}
public static void main(String[] args) throws InterruptedException {
AddThread at=new AddThread();
 at.start();
at.join();
System.out.println(i);
} }

如上面的代码,希望在主线程中加入at方法,在at方法执行完毕之后再执行System.out.println(i);这句话。

join(有时间和没时间的,如果有无参数就是无限等待,有参数,就是等待一段时间如果还没有结束就继续往下执行,我等不起。)
join的本质

while (isAlive()) {
wait(0); 
}

如果线程是isalive(没死掉的),就无线等待,直到线程执行完毕后, 系统会调用 notifyAll(),注意 ** 不要在Thread实例上使用 wait()和notify()方法

守护线程

在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程
当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出
Thread t=new DaemonT(); t.setDaemon(true); t.start();
举例子:

psvm(){
Thread t =new DeamonT();
t.setDaemon(true);
t.start();
}

会发现,刚启动程序,就自动停掉了。