多线程入门(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/by_perseverance/article/details/86216912
  • 1、进程与线程

定义:

进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统构成的基础。每个正在系统中运行的程序都是一个进程。

线程:线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

区别:

1、进程是所有线程的集合,每一个线程是进程中的一条执行路径;

2、进程是资源分配的最小单位,线程是程序执行的最小单位;

3、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多;

4、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。

  • 2、使用场景及实现方式

使用场景:

1、后台任务,例如:定时向大量(100w以上)的用户发送邮件、短信,迅雷多线程下载。

2、异步处理,例如:发微博、记录日志等;

3、分布式计算。

实现方式:

1、继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。

代码:

扫描二维码关注公众号,回复: 4858427 查看本文章
class CreateThread extends Thread {
	// run方法中编写 多线程需要执行的代码
	public void run() {
		for (int i = 0; i< 10; i++) {
			System.out.println("i:" + i);
		}
	}
}
public class ThreadDemo {
	public static void main(String[] args) {
		System.out.println("-----多线程创建开始-----");
		// 1.创建一个线程
		CreateThread createThread = new CreateThread();
		// 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
		System.out.println("-----多线程创建启动-----");
		createThread.start();
		System.out.println("-----多线程创建结束-----");
	}
}

2、实现Runnable接口创建线程

如果自己的类已经extends另一个类,就无法直接extends Thread(注:Java语言的特点“单根继承”),此时,可以实现一个Runnable接口。

代码:

//功能描述:(创建多线程例子-Thread类 重写run方法)
class CreateRunnable implements Runnable {

	@Override
	publicvoid run() {
		for (inti = 0; i< 10; i++) {
			System.out.println("i:" + i);
		}
	}
}

//功能描述:(创建多线程例子-Thread类 重写run方法)
public class ThreadDemo2 {
	public static void main(String[] args) {
		System.out.println("-----多线程创建开始-----");
		// 1.创建一个线程
		CreateRunnable createThread = new CreateRunnable();
		// 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
		System.out.println("-----多线程创建启动-----");
		Thread thread = new Thread(createThread);
		thread.start();
		System.out.println("-----多线程创建结束-----");
	}
}

3、使用匿名内部类方式

代码:

public class InnerClassThread {
	
	public static void main(String[] args) {
		 System.out.println("-----多线程创建开始-----");
		 Thread thread = new Thread(new Runnable() {
			public void run() {
				for (int i = 0; i< 10; i++) {
					System.out.println("i:" + i);
				}
			}
		});
		 thread.start();
		 System.out.println("-----多线程创建结束-----");
	}
}
  • 3、线程的运行状态

状态分类:

Java中的线程大致存在六种状态,有的说是五种(除去了“超时等待”)。

1、初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

2、运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

3、阻塞(BLOCKED):表示线程阻塞于锁。

4、等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

5、超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒

6、终止(TERMINATED):表示该线程已经执行完毕。

状态切换图:

  • 4、线程的API方法

Thread类中的属性:

private volatile String name;//线程的名称
private int            priority;//线程的优先级,最小1、默认5、最大10
private boolean     daemon = false;//是否是守护线程
private Runnable target;    //该线程要执行的任务
private ThreadGroup group;    //该线程归属的线程组
private ClassLoader contextClassLoader;    //加载器,默认为AppClassLoader
private static int threadInitNumber;    //数字标识匿名线程
ThreadLocal.ThreadLocalMap threadLocals = null;    //线程私有变量
private long stackSize;    //分配给线程的栈空间大小,默认0(不限制)
private long tid;    //线程id
private static long threadSeqNumber;    //线程序列
private volatile int threadStatus = 0;    //线程运行状态,默认是0(NEW)
 
volatile Object parkBlocker;    //java.util.concurrent.locks.LockSupport.park使用
private volatile Interruptible blocker;    //枚举字段,interrupt方法使用

Thread的构造方法:

public Thread();
public Thread(Runnable target);
public Thread(Runnable target,String name);
public Thread(String name);
public Thread(ThreadGroup group, Runnable target);
public Thread(ThreadGroup group, Runnable target, String name);
/*分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。*/
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) 
//ThreadGroup如果不指定,则为当前线程(即启动该线程的线程)的ThreadGroup;Runnable就是该线程将要执行的类,
//分两种情况,一种是继承了Thread类,一种是实现了Runnable接口;name就是线程的名称,如果不指定则为"Thread-索引号(索引从0开始)";stackSize是JVM分配给线程的栈空间大小,默认0(不限制)

Thread中的关于自身的API:

//返回对当前正在执行的线程对象的引用
public static native Thread currentThread() 
//测试当前线程是否中断
public static boolean interrupted()
//使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)
public static native void sleep(long millis) throws InterruptedException;
//获取该线程状态,返回值为枚举,包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED
public State getState()
//判断该线程是否存活,标准是线程已启动但还没有执行完毕
public final native boolean isAlive()
//获取当前jvm中该线程的唯一标示
public long getId()
//判断该线程是否是守护线程
public final boolean isDaemon()
//设置线程的名称
public final synchronized void setName(String name)
//获取线程的名称
public final String getName()

Thread类中关于执行的API:

//正确的启动一个线程的方法。先完成一些初始化动作,然后会通知JVM调用线程的run()方法
public synchronized void start()
//只是单纯地执行线程代码,并没有启动一个新的线程
public void run() 
//保证指定时间以内,在JVM中只有该线程在运行。millis是等待的毫秒数,nanos是微秒数(参数可省)。
//如果millis填0,则一直等到当前线程执行完毕,JVM才会调度其他线程执行
public final synchronized void join(long millis, int nanos)
//让该线程运行结束之前做下自身资源清理工作
private void exit()
//当该线程让出CPU,给其他线程一个获取调度的机会,但是不确保一定会调度到其他线程上
public static native void yield();
//判断该线程中断标识是否为true,返回该标识并将标识置为false
public static boolean interrupted()
//判断该线程中断标识是否为true,返回该标识。其实该方法和interrupted()方法底层调用的都是
//isInterrupted(boolean isClearState),只不过参数不同而已
public boolean isInterrupted()
//它不会中断一个正在运行的线程,实际上是在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞
//的状态。更确切的说,如果线程被wait、join或sleep阻塞时,那么它将接收到一个中断异常
//(InterruptedException),我们可以在异常块里调用本方法,从而提早地终结被阻塞状态。如果线程没有
//被阻塞,这时调用interrupt()将不起作用,仅仅是设置中断标志位为true的情况
public void interrupt()
  • 5、几种方法比较:

1、Thread.sleep(long millis)

一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。

2、Thread.yield()

暂停当前正在执行的线程,并执行其他线程(可能没有效果)。让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。

3、thread.join()/thread.join(long millis)

join作用是让其他线程变为等待,    t1.join();// 让其他线程变为等待,直到当前t1线程执行完毕,才释放。

把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

4、obj.wait()

当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。

5、obj.notify()

唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

  • 6、例题:现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行 

思路:使用join方法实现

代码:

public class JoinThreadDemo02 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				for (int i = 0; i < 20; i++) {
					System.out.println("t1,i:" + i);
				}
			}
		});
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				try {
					t1.join();
				} catch (Exception e) {
					// TODO: handle exception
				}
				for (int i = 0; i < 20; i++) {
					System.out.println("t2,i:" + i);
				}
			}
		});
		Thread t3 = new Thread(new Runnable() {
			public void run() {
				try {
					t2.join();
				} catch (Exception e) {
					// TODO: handle exception
				}
				for (int i = 0; i < 20; i++) {
					System.out.println("t3,i:" + i);
				}
			}
		});
		t1.start();
		t2.start();
		t3.start();
	}
}

猜你喜欢

转载自blog.csdn.net/by_perseverance/article/details/86216912