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可以在任意对象及方法上加锁,加锁的这段代码称为“互斥区”或者“临界区”

时间: 2024-10-20 18:01:09

java多线程入门学习(一)的相关文章

Java多线程技术学习笔记(二)

目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和wait的区别 停止线程的方式 守护线程 线程的其他知识点 一.线程间的通信示例 返目录回 多个线程在处理同一资源,任务却不同. 假设有一堆货物,有一辆车把这批货物往仓库里面运,另外一辆车把前一辆车运进仓库的货物往外面运.这里货物就是同一资源,但是两辆车的任务却不同,一个是往里运,一个是往外运. 下面

(转载)Java多线程入门理解

转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢?如果你觉得此文很简单,那推荐你看看Java并发包的的线程池(Java并发编程与技术内幕:线程池深入理解),或者看这个专栏:Java并发编程与技术内幕.你将会对Java里头的高并发场景下的线程有更加深刻的理解. 目录(?)[-] 一扩展javalangThread类 二实现javalan

Java Web入门学习路线图的规划

Java作为学习编程开发入门语言,可能对于许多新手来说可能有点摸不着北,做位一名有几年开发经验的老鸟,希望给一些新人分享经验,当然其他老鸟如果有什么意见可以指出,我也会努力纠正. 本人工作是有关Java Web 开发,所以我会以Java Web标准开发去做一个标准,当然Java 能做的事情很多,包括Android,Java Me等,后期只要我们努力可以学习更多,更深的我也说不了,我现在只谈谈怎么去入门,至于登堂入室,大家有了套路以后,都能够达到.我写这个初衷,是因为我本来是一名非计算机专业的学生

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

java多线程使用学习笔记

初学Java多线程,后续继续改进 一,Callable Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务 Callable和Runnable的区别如下: 1.Callable定义的方法是call,而Runnable定义的方法是run. 2.Callable的call方法可以有返回值,而Runnable的run方法不能有返回值. 3.Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常.

Java多线程技术学习笔记(一)

目录: 概述 多线程的好处与弊端 JVM中的多线程解析 多线程的创建方式之一:继承Thread类 线程的状态 多线程创建的方式之二:实现Runnable接口 使用方式二创建多线程的好处 多线程示例 线程安全问题现象 线程安全问题产生的原因 同步代码块 同步的好处与弊端 同步的前提 同步函数 验证同步函数的锁 单例模式的线程安全问题的解决方案 死锁示例 一.概述 目录 首先得了解进程,打开我们电脑的windows资源管理器,可以直观看到进程的样子: 进程直观上理解就是正在进行的程序.而每个进程包含

Java语言入门学习

1.Java入门 2.数据类型与数组 3.运算符.表达式 4.控制语句 5.类与对象 6.子类与继承 7.接口与实现 8.内部类与异常类 9.常用实用类 10.组件与事件处理 11.输入输出流 12.jdbc数据库操作 13.多线程机制 14.网络编程

JAVA多线程基础学习三:volatile关键字

Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个关键字的作用就是告诉编译器,只要是被此关键字修饰的变量都是易变的.不稳定的.那为什么是易变的呢?因为volatile所修饰的变量是直接存在于主内存中的,线程对变量的操作也是直接反映在主内存中,所以说其是易变的. 一.Java内存模型 Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理

JAVA多线程入门

为什么使用多线程 进程之间不能共享内存,但线程之间共享内存很容易 系统创建进程需要为该进程重新分配系统资源,但创建线程代价小得多,因此使用多线程来实现多任务并发比多进程的效率高 JAVA内置了多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化了JAVA的多线程编程 线程的创建 继承Thread类(可直接使用this关键字获得当前对象,多个线程无法共享线程类的实例变量) 实现Runnable接口(必须使用Thread.currentThread()方法,多个线程可以共享线程类的实例变