首页 > 代码库 > Java线程小记

Java线程小记

线程是程序内部不同的执行分支,实现多线程主要有两种方式:继承自Thread类或实现Runnable接口,其实Thread类也是实现了Runnable接口,基于Java的单继承机制,我们建议使用实现Runnable的方式来实现线程类,Runnable接口里面只有一个run方法,线程类必须实现这个方法,多线程的业务逻辑就是写在run方法里面实现的。

线程状态的转换:

技术分享

Thread t = new Thread(runnable);

这样就创建了一个线程,注意runnable是实现了Runnable接口的线程类

t.start();

线程进入就绪状态,何时被调用取决于CPU的“心情”(线程的优先级,CPU的繁忙度等等),CPU调度该线程使线程成为运行状态,执行线程类的run方法。线程在运行状态的时候有可能进入阻塞状态,例如代码中调用了Thread.sleep()/wait()或程序抛出了InterruptedException等等,当线程不满足某些条件的时候,线程会自动退出,强烈不建议使用已经废弃的stop方法来强杀线程,这样可能会引起很多问题,例如有些资源可能再也不会被适当地关闭了。

isAlive(): 检查该线程是否还活着,一个线程如果已经启动还未终止的话,那就是活着的状态。

public class IsAlive {    public static void main(String[] args) throws InterruptedException {        MyRunnable myRunnable = new MyRunnable();        Thread myThread = new Thread(myRunnable);        System.out.println("1. myThread isAlive ? " + myThread.isAlive());        myThread.start();        System.out.println("2. myThread isAlive ? " + myThread.isAlive());        Thread.sleep(3000);        System.out.println("3. myThread isAlive ? " + myThread.isAlive());    }}class MyRunnable implements Runnable {    @Override    public void run() {        for (int i = 0; i < 5; i++) {            System.out.println("MyRunnable.run i = " + i);        }    }}

技术分享

setPriority和getPriority方法的作用分别是设置线程优先级和获取线程优先级,线程是有优先级的,级别分别是从1到10,1为最低级别,线程创建后默认的优先级为5,Thread类定义了3个int常量来表示线程的最低,最高和默认级别:

    /**     * The minimum priority that a thread can have.     */    public final static int MIN_PRIORITY = 1;   /**     * The default priority that is assigned to a thread.     */    public final static int NORM_PRIORITY = 5;    /**     * The maximum priority that a thread can have.     */    public final static int MAX_PRIORITY = 10;
public class Priority {    public static void main(String[] args) {        MyRunnable1 myRunnable = new MyRunnable1();        Thread myThread = new Thread(myRunnable);        System.out.println("默认级别: " + myThread.getPriority());        myThread.setPriority(Thread.NORM_PRIORITY + 3);        System.out.println("新级别: " + myThread.getPriority());    }}class MyRunnable1 implements Runnable {    @Override    public void run() {}    }

技术分享

线程的优先级别越高,则CPU为其分配的时间片就越多。

Thread.sleep(): 让当前线程睡眠指定毫秒数

使用Thread.sleep(1000)来做一个类似计时器的功能

public class Sleep {    public static void main(String[] args) {        MyRunnable3 myRunnable = new MyRunnable3();        Thread myThread = new Thread(myRunnable);        myThread.start();    }}class MyRunnable3 implements Runnable {    @Override    public void run() {        for (int i = 0; i < 5; i++) {            System.out.println(new Date());            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

技术分享

join(): 将线程加入到当前主线程来执行,等待加入线程的结束,就相当于方法调用。

如果我不使用join方法,你会看到主线程和分支线程是并发运行的:

public class Join {    public static void main(String[] args) throws InterruptedException {        MyRunnable4 myRunnable = new MyRunnable4();        Thread myThread = new Thread(myRunnable);        myThread.start();//        myThread.join();        for (int i = 0; i < 3; i++) {            System.out.println("我也要跑");            Thread.sleep(500);        }    }}class MyRunnable4 implements Runnable {    @Override    public void run() {        for (int i = 0; i < 3; i++) {            System.out.println("跟着我跑");            try {                Thread.sleep(500);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

技术分享

如果现在我反注释掉myThread.join()那一行,你会发现程序会先执行分支线程再执行主线程中的打印语句:

技术分享

这个效果跟如下代码产生的效果是一样的,也就是普通的方法调用:

public class Join {    public static void main(String[] args) throws InterruptedException {        MyRunnable4 myRunnable = new MyRunnable4();        myRunnable.run();        for (int i = 0; i < 3; i++) {            System.out.println("我也要跑");            Thread.sleep(500);        }    }}class MyRunnable4 implements Runnable {    @Override    public void run() {        for (int i = 0; i < 3; i++) {            System.out.println("跟着我跑");            try {                Thread.sleep(500);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

yield(): 让线程“高风亮节”让出CPU资源给其他线程执行。

死锁问题的产生原理:

技术分享

假设我们有两个线程Thread1和Thread2,有两个synchronized的对象ObjA和ObjB, Thread1的执行需要先锁定ObjA再锁定ObjB来执行操作,然后才能完成任务并释放锁,Thread2则相反,它需要先锁定ObjB再锁定ObjA才能完成任务,当Thread1执行的时候发现ObjB被Thread2锁住了,所以只能等待,而Thread2视图去锁定ObjA的时候发现该对象被Thread1锁住了,所以Thread2也只能等待,于是两个对象都在等待对方释放锁,这就造成了死锁,死锁的解决方案之一是实现粗粒度的锁,不要同时在多个对象上加锁。

synchronized: 来看看一道经典的面试题

 1 public class ThreadInterview implements Runnable { 2      3     private int i = 100; 4      5     public synchronized void foo1() throws InterruptedException { 6         i = 1000; 7         Thread.sleep(3000); 8         System.out.println("foo1 i = " + i); 9     }10     11     public void foo2() {12         System.out.println("foo2 i = " + i);13     }14 15     public static void main(String[] args) throws InterruptedException {16         ThreadInterview ti = new ThreadInterview();17         Thread t = new Thread(ti);18         t.start();19         Thread.sleep(1000);20         ti.foo2();21     }22 23     @Override24     public void run() {25         try {26             foo1();27         } catch (InterruptedException e) {28             e.printStackTrace();29         }30     }31 }

问20行i的值是多少?

答案是1000,为什么不是100,该题的考点在哪里?foo1是一个同步的方法,当线程t执行这个方法的时候会将ti对象锁定,但是foo2不是同步方法,main线程还是可以通过foo2访问i这个值得,由于执行顺序先后问题,i在foo1被执行的时候就改为1000了,当然foo2中的i也是1000了。考点就是,同步方法会锁定对象当时其它线程依然可以在同时访问其非同步方法。

Thread.sleep()跟Object.wait()的区别?

当线程sleep的时候,线程会在指定毫秒数之后醒过来,线程依然持有锁;线程wait的时候,线程不会主动醒过来除非有调用该线程的notify方法,wait的时候,锁不再归该线程所有。

notify方法:叫醒当前正在wait的线程。

Java线程小记