Java多线程

来源:互联网 发布:php authorization 编辑:程序博客网 时间:2024/06/05 02:43

1. 实例变量与线程安全
自定义线程类中的实例变量针对其他线程有共享与不共享之分,这在多个线程之间交互时是一个很重要的技术点。
不共享数据的情况:每个线程都有各自的实例变量,多个线程交互时不影响各自的实例变量值,不存在线程安全问题。
共享数据的情况:多个线程访问同一个实例变量,多个线程交互时存在“非线程安全问题”。
“非线程安全”是指多个线程对同一个对象中的实例变量进行操作时值被更改、值不同步的情况,进而影响程序的执行流程。
解决“非线程安全”可以使用synchronized关键字。

2. i–或i++与System.out.println()的异常
看下面例子中,i++与println()联合使用时有可能出现的异常情况:

public class MyThread implements Runnable{    private Integer count = 0;    @Override    public void run() {        System.out.println(Thread.currentThread().getName()                 + " count = " + (count++));    }    public static void main(String[] args) {        MyThread myThread = new MyThread();        Thread thread1 = new Thread(myThread);        Thread thread2 = new Thread(myThread);        Thread thread3 = new Thread(myThread);        Thread thread4 = new Thread(myThread);        thread1.start();        thread2.start();        thread3.start();        thread4.start();    }}

打印结果:

Thread-0 count = 0Thread-2 count = 3Thread-3 count = 2Thread-1 count = 1

而如果改变run()方法成这样:

@Overridepublic void run() {    count++;    System.out.println(Thread.currentThread().getName()             + " count = " + count);}

则打印结果:

Thread-0 count = 1Thread-3 count = 3Thread-2 count = 2Thread-1 count = 4

由上面实验可知:虽然println方法内部是同步的,但是i++操作是在进入println方法之前发生的,所以有发生非线程安全的概率。

public void println(String x) {    synchronized (this) {        print(x);        newLine();    }}

而i++操作是非原子的,在某些JVM中,i++要分为三步:
a. 取得原有i的值;
b. 计算i + 1;
c. 对i进行赋值;
这三步中,如果有多个线程访问,那么一定会出现非线程安全问题。

为了线程安全,还是需要使用同步方法。

3. currentThread()方法
currentThread()方法可以返回代码段正在被哪个线程调用的信息。

public class MyThread5 extends Thread{    public MyThread5() {        System.out.println("construction begin");        System.out.println("Thread.currentThread().getName():"                + Thread.currentThread().getName());        System.out.println("this.getName():" + this.getName());        System.out.println("contruction end");    }    @Override    public void run() {        System.out.println("run begin");        System.out.println("Thread.currentThread().getName():"                + Thread.currentThread().getName());        System.out.println("this.getName():" + this.getName());        System.out.println("run end");    }    public static void main(String[] args) {        MyThread5 myThread5 = new MyThread5();        Thread thread = new Thread(myThread5);        thread.setName("A");        thread.start();    }}

输出结果:

construction beginThread.currentThread().getName():mainthis.getName():Thread-0contruction endrun beginThread.currentThread().getName():Athis.getName():Thread-0run end

这里的this指该线程类的实例,改线程类继承Thread类,没有对Name重命名,所以在run()方法中通过this.getName()输出的是默认名称:Thread-0。
注意:如果将main方法中创建线程并启动的过程改成下面这种,那么run()方法中的this就代表当前线程:

public static void main(String[] args) {    MyThread5 myThread5 = new MyThread5();    /*Thread thread = new Thread(myThread5);    thread.setName("A");    thread.start();*/    myThread5.setName("A");    myThread5.start();}

输出结果:

construction beginThread.currentThread().getName():mainthis.getName():Thread-0contruction endrun beginThread.currentThread().getName():Athis.getName():A // 区别run end

4. isAlive()
isAlive()用于判断当前线程是否处于活动状态。活动状态是指线程已经启动且尚未终止。线程处于运行或者就绪状态,就认为线程是“存活”的。
看下面例子通过isAlive()方法区分this和Thread.currentThread()。

public class MyThread extends Thread{    public MyThread() {        System.out.println("构造方法 begin");        System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive():" + Thread.currentThread().isAlive());        System.out.println("this.getName():" + this.getName());        System.out.println("this.isAlive():" + this.isAlive());        System.out.println("构造方法 end");    }    @Override    public void run() {        System.out.println("run begin");        System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive():" + Thread.currentThread().isAlive());        System.out.println("this.getName():" + this.getName());        System.out.println("this.isAlive():" + this.isAlive());        System.out.println("run end");    }    public static void main(String[] args) {        MyThread myThread = new MyThread();        Thread thread = new Thread(myThread);        thread.setName("A");        thread.start();    }}

输出结果:

构造方法 beginThread.currentThread().getName():mainThread.currentThread().isAlive():truethis.getName():Thread-0this.isAlive():false构造方法 endrun beginThread.currentThread().getName():AThread.currentThread().isAlive():truethis.getName():Thread-0this.isAlive():falserun end

如果main方法改成如下:

public static void main(String[] args) {    MyThread myThread = new MyThread();    myThread.setName("A");    myThread.start();}

输出结果:

构造方法 beginThread.currentThread().getName():mainThread.currentThread().isAlive():truethis.getName():Thread-0this.isAlive():false构造方法 endrun beginThread.currentThread().getName():AThread.currentThread().isAlive():truethis.getName():A // 区别this.isAlive():true // 区别run end

将线程对象以构造参数的方式传递给Thread对象进行start()启动线程,我们直接启动的线程是thread,而作为构造参数的myThread,赋值给Thread对象的target,之后通过thread的run方法调用target.run()。此时Thread.currentThread是Thread的引用thread,而this则表示myThread。

5. sleep()
sleep()的作用是在指定毫秒时间内,让当前正在执行的线程休眠(暂停执行)。这个正在执行的线程是指this.currentThread()返回的线程(Thread.currentThread())。

6. getId()
getId()方法的作用的取的线程的唯一标识。比如thread-0的0。

7. yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

上述内容总结自《Java多线程编程核心技术》一书。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 几年前被犬咬没打针怎么办 对狂犬疫苗过敏怎么办 想去墨尔本留学怎么办 大学没交学费会怎么办 交学费收据丢了怎么办 上海浦东金科苑幼儿园统筹怎么办 红酒瓶塞掉了怎么办 红酒瓶塞丢了怎么办 收入证明没有公章怎么办 父母没工作怎么办签证 在温哥华怎么办新西兰签证 上班第一天无聊怎么办 上班紧张不自信怎么办 销售到了30岁怎么办 转行会计没经验怎么办 澳洲coe被取消怎么办 父母不同意嫁远怎么办 家人不同意我们在一起怎么办 语言课申请不到怎么办 墨尔本大学均分不够怎么办 去澳洲旅游签证怎么办 换新护照澳大利亚签证怎么办 大学错过交学费日期怎么办 留学加拿大申请工签被拒怎么办 英国大学挂科怎么办 小孩子学不进去怎么办 大学生学不进去怎么办 高考没考上本科怎么办 想读书家里没钱怎么办 考上大学没钱上怎么办 儿子没考上大学怎么办 后悔没上大学怎么办 把学费花了怎么办 临床预科挂科怎么办 补录了预科怎么办 美国预科没过怎么办 出国留学报到证怎么办 本科绩点2.93怎么办 在美国被抢劫怎么办 去美国留学费用怎么办 被美国大学停学怎么办