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可以在任意对象及方法上加锁,加锁的这段代码称为“互斥区”或者“临界区”
本文介绍Java多线程的基础概念,包括进程与线程的区别、线程生命周期等,并通过实例演示如何通过继承Thread类和实现Runnable接口来创建多线程。
238

被折叠的 条评论
为什么被折叠?



