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{@Overridepublic 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 overthis is my first Thread : MyThread
上面介绍了线程调用的随机性,以下这个样例将展示线程的随机性:
package cn.sun.thread;public class MyThread extends Thread{@Overridepublic 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 mainrun MyThreadrun mainrun mainrun MyThreadrun MyThreadrun mainrun MyThreadrun mainrun mainrun MyThreadrun mainrun MyThreadrun MyThreadrun MyThreadrun mainrun mainrun MyThreadrun mainrun MyThread
从上面就能够看出来线程的随机性,为什么会出现这样的形式(随机性),主要是由于我们运行的是它的start()方法。当运行start()时将通知“线程规划器”此线程已准备好。等待调用run()方法,也就是让系统安排一个时间去运行它。达到异步的效果。假设我们直接运行run()方法,则没有这样的异步的效果。直接同步了,多线程亦没有了意义。
有一点须要说明,当我们同一时候启动多个线程时,运行start()方法的顺序不代表线程启动的顺序,还是那句话。真正的运行依赖于系统所分配的时间片。
三.实现Runnable接口
此类的应用场景相信大家已经知道了,就是当某个类已经有一个父类时,无法再继承Thread类,我们就用Runnable接口去实现它
package cn.sun.runable;public class MyRunnable implements Runnable{@Overridepublic 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);}@Overridepublic 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 :4A's count is :3A's count is :2A's count is :1A's count is :0C's count is :4C's count is :3C's count is :2B's count is :4B's count is :3B's count is :2C's count is :1C's count is :0B's count is :1B's count is :0
3.2 共享数据的情况
上一个样例并不存在多个线程訪问同一个实例变量的情况,这里我们试试三个线程对同一变量进行操作:
package cn.sun.safe;public class MyThreadShare extends Thread {private int count = 5;@Overridepublic 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 :3C's count is :2A's count is :3D's count is :1E'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;@Overridesynchronized public void run() {super.run();count--;System.out.println(this.currentThread().getName()+"'s count is :" +count);}}
这样之后执行结果就呈现这种样子:
A's count is :4C's count is :3B's count is :2E's count is :1D's count is :0
synchronized能够在随意对象及方法上加锁,加锁的这段代码称为“相互排斥区”或者“临界区”