java多线程入门学习(一)

java多线程入门学习(一)

一.java多线程之前

进程:每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。一个进程包括1--n个线程。

      线程:同一类线程共享代码和数据空间,每一个线程有独立的执行栈和程序计数器(PC),线程切换开销小。

      线程和进程一样分为五个阶段:创建、就绪、执行、堵塞、终止。

      多进程是指操作系统能同一时候执行多个任务(程序)。

      多线程是指在同一程序中有多个顺序流在运行。

在java中要想实现多线程,有两种手段。一种是继承Thread类,第二种是实现Runnable接口。为什么会有两种方式。原因是因为java具有单继承性质,当有些类已经实现继承。但须要多线程时,我们给它第二种办法。实现Runnable接口


二.继承Thread类

          在学习怎样创建新的线程曾经。首先让我们看下Thread类的结构:
public class Thread implements Runnable
        从上面的源代码中能够发现Thread类实现了Runnable接口,它们之间是多态的关系。
        
        下面開始写代码,继承Thread类,我们在run()方法中写线程要运行的任务代码: 
package cn.sun.thread;
 
public class MyThread extends Thread{
 
@Override
public void run() {
super.run();
System.out.println("this is my first Thread : MyThread");
}
}
        測试类例如以下,尽管我们复写的是run()方法。可是运行的却是start()方法:
package cn.sun.test;
 
import cn.sun.thread.MyThread;
 
public class ThreadTest01 {
public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();
System.out.println("program over");
}
 
}
        从以下运行结果来看,能够知道使用多线程时代码的运行结构与代码运行顺序无关:
program over
this is my first Thread : MyThread

        上面介绍了线程调用的随机性,以下这个样例将展示线程的随机性:
package cn.sun.thread;
 
public class MyThread extends Thread{
 
@Override
public void run() {
try {
for(int i=0; i<10; i++){
int time = (int) (Math.random()*1000);
Thread.sleep(time);
System.out.println("run="+Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

package cn.sun.thread;
 
public class MyThreadTest02 {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.setName("myThread");
thread.start();
for(int i=0; i<10; i++){
int time = (int) (Math.random()*1000);
Thread.sleep(time);
System.out.println("run="+Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果例如以下:
run main
run MyThread
run main
run main
run MyThread
run MyThread
run main
run MyThread
run main
run main
run MyThread
run main
run MyThread
run MyThread
run MyThread
run main
run main
run MyThread
run main
run MyThread


        从上面就能够看出来线程的随机性,为什么会出现这样的形式(随机性),主要是由于我们运行的是它的start()方法。当运行start()时将通知“线程规划器”此线程已准备好。等待调用run()方法,也就是让系统安排一个时间去运行它。达到异步的效果。假设我们直接运行run()方法,则没有这样的异步的效果。直接同步了,多线程亦没有了意义。
        有一点须要说明,当我们同一时候启动多个线程时,运行start()方法的顺序不代表线程启动的顺序,还是那句话。真正的运行依赖于系统所分配的时间片。

三.实现Runnable接口

此类的应用场景相信大家已经知道了,就是当某个类已经有一个父类时,无法再继承Thread类,我们就用Runnable接口去实现它
package cn.sun.runable;
 
public class MyRunnable implements Runnable{
 
@Override
public void run() {
System.out.println("MyRunnable run...");
}
 
}

package cn.sun.test;
 
import cn.sun.runable.MyRunnable;
 
public class MyRunnableTest01 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("main over...");
}
 
}
结果例如以下:
main over...
MyRunnable run...


在上面的代码中,Thread
thread =newThread(runnable);的代码不仅能够传入一个Runnable对象。还能够传入一个Thread类对象,这样做能够将一个Thread对象中的run()方法交给其它的线程进行调用。

三.实例变量的共享与私有

3.1 数据不共享形式:

package cn.sun.safe;
 
public class MyThread extends Thread {
private int count = 5;
public MyThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while(count > 0){
count--;
System.out.println(this.currentThread().getName()+"'s count is :" +count);
}
}
 
}

package cn.sun.test;
 
import cn.sun.safe.MyThread;
 
public class ThreadTest02 {
public static void main(String[] args) {
MyThread threadA = new MyThread("A");
MyThread threadB = new MyThread("B");
MyThread threadC = new MyThread("C");
threadA.start();
threadB.start();
threadC.start();
}
 
}
结果例如以下,各个实例对象的实例变量保持了私有。并没有共享:
A's count is :4
A's count is :3
A's count is :2
A's count is :1
A's count is :0
C's count is :4
C's count is :3
C's count is :2
B's count is :4
B's count is :3
B's count is :2
C's count is :1
C's count is :0
B's count is :1
B's count is :0

3.2 共享数据的情况

        上一个样例并不存在多个线程訪问同一个实例变量的情况,这里我们试试三个线程对同一变量进行操作:

package cn.sun.safe;
 
public class MyThreadShare extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
count--;
System.out.println(this.currentThread().getName()+"'s count is :" +count);
}
 
}

package cn.sun.test;
 
import cn.sun.safe.MyThreadShare;
 
 
 
public class ThreadTest03 {
public static void main(String[] args) {
MyThreadShare myThread = new MyThreadShare();
Thread a = new Thread(myThread,"A");
Thread b = new Thread(myThread,"B");
Thread c = new Thread(myThread,"C");
Thread d = new Thread(myThread,"D");
Thread e = new Thread(myThread,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
 
}
结果发现这种情况:
B's count is :3
C's count is :2
A's count is :3
D's count is :1
E's count is :0

        其中的3产生了非线程安全问题。有两个线程打印出相同的数字,这样肯定是不行的。我们应该出现的效果是依次递减,而不是反复数字递减
        这里我们须要了解这样一个事情,在有些JVM中 i-- 的操作分为例如以下三步:
                (1)取得原有i的值
                (2)计算 i-1
                (3)对i进行赋值
        当多个线程同一时候进来訪问时,肯定会出现非线程安全问题,我们将这个步骤用synchronized进行同步化就能够解决问题
package cn.sun.safe;
 
public class MyThreadShare extends Thread {
private int count = 5;
@Override
synchronized public void run() {
super.run();
count--;
System.out.println(this.currentThread().getName()+"'s count is :" +count);
}
 
}
        这样之后执行结果就呈现这种样子:
A's count is :4
C's count is :3
B's count is :2
E's count is :1
D's count is :0
        synchronized能够在随意对象及方法上加锁,加锁的这段代码称为“相互排斥区”或者“临界区”


上一篇:兼容 Android 4.4 透明状态栏与导航栏


下一篇:Razor练习2