1.什么是线程
3.1 新建线程
线程的构造器
3.2 线程的终止
源码:
来看个小demo,再结合上面图理解下
– 线程是进程内的执行单元
进程的切换是一个重量级的操作,如果使用多进程去做并行,并发度不可能是很高的
而线程是进程里更小的一个执行单元,所以线程可以较为广泛的用来做并发程序的设计
2.线程的生命周期
/**
* 线程的所有状态都在Thread中的State枚举中定义
* @author mj
*
*/
public enum State {
//新建状态,并没有开始工作,只是一个静态的实体
NEW,
//一切准备就绪,可以执行了.但是不表示该线程一定在CPU上执行了,取决于物理CPU的调度
RUNNABLE,
//阻塞
BLOCKED,
//等待,一种是无限期等待,一种是有限等待
WAITING,
TIMED_WAITING,
//终止状态
TERMINATED;
}
3.1 新建线程
new一个线程,然后将它start()起来.
线程start以后,就会新建一个线程并让这个线程执行run()方法.
如果不调用start(),直接调用run(),就不能新建一个线程,而是在当前线程中直接调用run()方法,只是作为一个普通的方法
so,不要用run()来开启新线程,它只会在当前线程中串行执行run()中代码
Thread.run()的实现
public void run() {
if (target != null) {
target.run();
}
}
target是Runnable接口.Runnable接口只有一个run方法.
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
线程的构造器
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
init()方法:
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null);
}
可以看出来在新建一个无参线程的时候target是null;
Thread t = new Thread();
如果targe本身是不存在的,那么这个run方法就可以被重载
Thread t = new Thread(){
@Override
public void run(){
System.out.println("what u want to do");
}
};
t.start();
如果target不为空,就不去重载run方法,会自动调用target实例的run;
用到了上面的第二个构造器
Thread t = new Thread(new Thread());
t.start();
3.2 线程的终止
Thread.stop()不推荐使用,它会释放掉所有的monitor
@Deprecated
public final void stop() {
// The VM can handle all thread states
stop(new ThreadDeath());
}
/**
* 如果之前被这些监视器保护的任何对象处于不一致状态,其它线程看到的这些对象就会处于不一致状态。
* 这种对象被称为受损的 (damaged)。当线程在受损的对象上进行操作时,会导致任意行为。
* 这种行为可能微妙且难以检测,也可能会比较明显。不像其他未受检的(unchecked)异常,
* ThreadDeath 悄无声息的杀死及其他线程。因此,用户得不到程序可能会崩溃的警告。
* 崩溃会在真正破坏发生后的任意时刻显现,甚至在数小时或数天之后线程可以在几乎任何地方抛出 ThreadDeath 异常。
* 由于这一点,所有的同步方法和(代码)块将必须被考虑得事无巨细。
* 线程在清理第一个 ThreadDeath 异常的时候(在 catch 或 finally 语句中),
* 可能会抛出第二个。清理工作将不得不重复直到到其成功。保障这一点的代码将会很复杂
* @author majie
*
*/
public class ThreadDeath extends Error {
private static final long serialVersionUID = -4417128565033088268L;
}
可以看到stop()是被弃用的.主要是这个方法太过暴力.
强行把执行到一半的线程终止,可能会引起一些数据不一致的问题
stop会直接终止线程,并且会立即释放这个线程所持有的锁
以上操作就会使数据混乱而且不易查出
3.3中断线程
public void Thread.interrupt(); //中断线程
public Boolean Thread.isInterrupt(); //线程是否被中断
public static Boolean Thread.interrupted(); //线程是否被中断,被清除当前中断线程
Thread.interrupt();实例方法,会给线程设置一个中断标志位
Tread.isInterrupt(),也是一个实例方法,它会判断当前线程是否被中断(通过检查中断标志位)
Thread.interrupt()也是用来判断当前线程的中断状态,但同时会清楚当前线程的中断标志位状态
/**
* 这里用interrupt相当于只是过来打了个招呼,并没有中断
*/
public void run() {
while (true) {
Thread.yield();
}
}
t.interrupt();
调用了interrupt()以后,线程并没有中断,还是会继续循环下。
源码里对它的描述是:Just to set the interrupt flag。(仅仅是设立了一个中断标志)
/**
* 优雅的中断方式。
*/
public void run() {
while (true) {
// 判断是否有中断标志,有就break
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted");
break;
}
Thread.yield();
}
}
挂起(suspend) 和继续执行(resume)线程-----------
被废弃,不推荐使用
---- 被挂起的线程需要等到resume()以后才能继续执行 .
---- suspend()不会释放锁
----如果发生在resume()之前,则发生死锁
@Deprecated
public final void suspend() {
checkAccess();
suspend0();
}
@Deprecated
public final void resume() {
checkAccess();
resume0();
}
public final void checkAccess() {
//用来保障程序安全的
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccess(this);
}
}
至于更多源码,这里不表。
suspend的代码:
public class BadSuspend {
public static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String name) {
super.setName(name);
}
public void run() {
synchronized (u) {
System.out.println("in " + getName());
Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
执行结果:
in t1
in t2
然后主程序还卡着没有走完。
我们来看看当前java进程:
然后查看该进程下所有线程
发生这种结果很有可能是t2提前执行了resume.
等待线程结束(join)和谦让(yeild)
publi static native void yield();
一旦执行会使当前线程让出CPU.但要注意,让出CPU并不是表示当前线程不执行了.而是
让出以后再去争夺
public final void join() throws InterruptedException
public final synchrolized void join(long millis) throws InterruptedException
join()方法无限等待,会一直阻塞,知道目标线程执行完毕
join(times);给了一个最大的等待时间,如果超过等待时间,目标线程还在执行,当前线程也会因为"等不及了",而继续往下执行
守护线程:
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");
}
if (millis == 0) {
//判断线程是否已经结束
while (isAlive()) {
//没有结束的话就做一个等待,线程结束以后会调用notifyAll()方法
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
----在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程
---- 当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出,往往只是起到辅助的作用
---- 当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出,往往只是起到辅助的作用
Thread t=new Thread();
t.setDaemon(true);
t.start();
要在start之前设置守护线程。
线程的优先级:
/**
* 一个线程能拥有的最小的优先级
*/
public final static int MIN_PRIORITY = 1;
/**
* 线程默认优先级
*/
public final static int NORM_PRIORITY = 5;
/**
* 一个线程能拥有的最大优先级
*/
public final static int MAX_PRIORITY = 10;
优先级高的竞争到资源的概率大,仅仅只是概率,并不是一定优先级高的比优先级低的先执行
设置线程的优先级:
t1.setPriority(Thread.MAX_PRIORITY);
基本的线程同步操作:
synchronized:
– 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
– 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
– 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
– 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
– 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
指定加锁对象:
public class Demo implements Runnable{
static Demo instance = new Demo();
static int i = 0;
@Override
public void run() {
for(int j = 0;j<100000;j++){
synchronized (instance) {
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
结果应该是200000;
如果去掉同步操作,结果将会小于
200000;
作用在实例方法:
public class Demo implements Runnable{
static int i = 0;
/**
* 实例方法
* 会把锁放在当前对象实例上.
*/
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j = 0;j<100000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Demo instance1 = new Demo();
// Demo instance2 = new Demo();
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance1);
// Thread t2 = new Thread(instance2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
记住,一定要是同一个对象实例.
Object.wait()和Object.notify()
来看个小demo,再结合上面图理解下
public class WaitDemo{
final static Object object = new Object();
public static class T1 extends Thread{
public void run() {
//这里必须加锁
synchronized (object) {
System.out.println("t1 start!");
try {
System.out.println("wait for object");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 end");
}
}
}
public static class T2 extends Thread{
public void run() {
//这里必须加锁
synchronized (object) {
System.out.println("t2 start! notify one thread");
object.notify();
System.out.println("t2 end");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1();
T2 t2 = new T2();
t1.start();
t2.start();
}
}
执行结果:
t1 start!
wait for object
t2 start! notify one thread
t2 end
t1 end
当然,你也可以把wait()放在synchronized上面执行看看.那样理解可能更深刻
Object.notifyAll()和Object.notify()的区别:
notifyAll()会唤醒所有等待的线程
Object.wait()是随机唤醒一个等待的线程