【传智播客郑州校区】线程精进指南
文/传智播客郑州中心就业服务部
多线程的起步• 线程和进程的区别 – 线程是程序执行的一条路径, 一个进程中可以包含多条线程 • 多线程的并行和并发 – 多线程并发执行可以提高程序的效率, 可以同时完成多项工作 – 我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。 – 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。 • 在生活中的表现 – 一边听歌 ,一边看技术文章 – 使用迅雷下载电影 • 在JVM虚拟中的表现 – 使用虚拟机时的入口main方法是一个线程,以及我们的垃圾回收机制也是一个线程 • 在源码中的表现 – fuck the source code [AppleScript] 纯文本查看 复制代码 public class Thread implements Runnable {
//省略部分源码
...
//省略部分构造
//线程是通过构造传入Runnable对象
public Thread(Runnable target) {
//通过调用init方法 传入target
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
...
//把tager赋给成员变量
this.target = target;
setPriority(priority);
}
//自己重写的Runable实现的方法 ,如果外部传入target则使用外部的
@Override
public void run() {
if (target != null) {
target.run();
}
}
} • 分析源码可知如下结论 – 看Thread类的构造函数,传递了Runnable接口的引用 – 通过init()方法找到传递的target给成员变量的target赋值 – 查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法 线程的进击• 重要的方法 – Thread.sleep(毫秒,纳秒) 线程睡眠(时间) [AppleScript] 纯文本查看 复制代码 public static void sleep(long millis, int nanos) throws InterruptedException {
//省略部分代码
...
sleep(millis);
}
//调用本地sleep方法
public static native void sleep(long millis) throws InterruptedException;
–setDaemon() 守护线程,当其他线程都结束时执行此线程
public final void setDaemon(boolean on) {
checkAccess();
//通过调用isAlive判断是否有存活线程
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive(); • join() 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续 [AppleScript] 纯文本查看 复制代码 public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
//通过wait(0)死循环 让线程永远处于等待状态
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
} • yield() 礼让线程 但不是绝对的,取决于cpu的时机 [AppleScript] 纯文本查看 复制代码 /**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield(); • 继承的Object方法 wait() 方法导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法唤醒该线程,调用wait方法会释放对当前同步监视器的锁定 notify() 唤醒在此同步监视器上等待的单个线程,如果所有线程都在此同步监视器上等待,则会唤醒其中一个线程,选择是任意的,只有当前线程放弃对同步监视器的锁定后(即调用wait()方法),才可以执行被唤醒的线程 notifyAll() 唤醒在此同步监视器上等待的所有线程,只有当前线程放弃对同步监视器的锁定后,才可以执行被唤醒的线程 线程的进阶• 1.什么情况下需要同步 – 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步. – 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码. • 2.同步代码块 – 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块 – 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的 [AppleScript] 纯文本查看 复制代码 class Printer {
public static void print1() {
synchronized(Printer.class){
//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
}
}
/*
* 非静态同步函数的锁是:this
* 静态的同步函数的锁是:字节码对象
*/
public static synchronized void print2() {
System.out.print("传");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
}
} 线程安全问题• 多线程并发操作同一数据时, 就有可能出现线程安全问题 • 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作 [AppleScript] 纯文本查看 复制代码 public class DemoSynchronized {
/**
* @param args
* 需求:铁路售票,一共100张,通过四个窗口卖完.
*/
public static void main(String[] args) {
TicketsSeller t1 = new TicketsSeller();
TicketsSeller t2 = new TicketsSeller();
TicketsSeller t3 = new TicketsSeller();
TicketsSeller t4 = new TicketsSeller();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t4.setName("窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketsSeller extends Thread {
private static int tickets = 100;
static Object obj = new Object();
public TicketsSeller() {
super();
}
public TicketsSeller(String name) {
super(name);
}
public void run() {
while(true) {
synchronized(obj) {
if(tickets <= 0)
break;
try {
Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...这是第" + tickets-- + "号票");
}
}
}
} 传智播客·黑马程序员郑州校区地址 郑州市高新区长椿路11号大学科技园(西区)东门8号楼三层 联系方式 0371-56061160/61/62 来校路线:地铁一号线梧桐街站A口出
|