Java编写的程序都运行在 Java 虚拟机 (JVM) 中,在 JVM 的内部,程序的多任务是通过多线程来实现的。每用 java 命令启动一个 java 应用程序,就会启动一个 JVM 进程。在同一个 JVM 进程中,有且只有一个进程,就是它自己。在这个 JVM 环境中,所有程序代码的运行都是以线程来运行。
对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信 很容易,速度也很快。不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。
实现多线程的两种方式
一种是继承 Thread类,一种是实现 Runnable 接口。使用线程的时候主要注意下面两点,一是如何指定线程需要执行的方法,二是如何启动一个线程。新线程的创建跟启动都是通过java代码来触发的,是由java代码通知java平台,java平台再启动线程,启动新线程是一个很短的过程,对于主线程来说,只是通知java平台要启动新线程,通知完了就继续执行代码,不需要等待新线程结果,只有所有线程都退出了,程序才退出。 Thread是定义中 java.lang 包中的,一个类只要继承了 Thread 类并且重写了 run 方法,就可以实现多线程操作,如 下定义的MyThread 类,继承 Thread 类并重写 run 方法:
public class MyThread extends Thread { private String name; public MyThread(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; i++) { System.out.println("name:" + name + ",i=" + i); } } }
操作类 MyThreadTest:
public class MyThreadTest{ public static void main(String[] args) { MyThread thread1 = new MyThread("线程A"); MyThread thread2 = new MyThread("线程B"); thread1.run(); thread2.run(); } }
运行结果如下:
name:线程A,i=0
name:线程A,i=1
name:线程A,i=2
name:线程A,i=3
name:线程A,i=4
name:线程A,i=5
name:线程A,i=6
name:线程A,i=7
name:线程A,i=8
name:线程A,i=9
name:线程B,i=0
name:线程B,i=1
name:线程B,i=2
name:线程B,i=3
name:线程B,i=4
name:线程B,i=5
name:线程B,i=6
name:线程B,i=7
name:线程B,i=8
name:线程B,i=9
发现程序的运行是先运行线程 A完成后再运行线程 B ,不是交替运行,说明程序不是按多线程方式运行,而是按单线程方式运行,正确的启动多线程操作方法应该是调用 start() 方法, JVM 会自动调用该对象的 run() 方法,如上把线程操作类My ThreadTest 类的 run ()方法改为 start ()方法后,执行结果如下:
name:线程A,i=0
name:线程B,i=0
name:线程A,i=1
name:线程B,i=1
name:线程A,i=2
name:线程B,i=2
name:线程A,i=3
name:线程B,i=3
name:线程A,i=4
name:线程B,i=4
name:线程A,i=5
name:线程B,i=5
name:线程A,i=6
name:线程B,i=6
name:线程A,i=7
name:线程B,i=7
name:线程A,i=8
name:线程B,i=8
name:线程A,i=9
name:线程B,i=9
此时已经可以实现多线程调用,但是使用继承Thread 类实现多线程有个限制, java 语言只能继承一个类,而不能多继承,此时可以使用第二种方式实现,就是实现 Runnable 接口。 开发中很少使用继承Thread 类实现多线程操作的,一般都使用实现 Runnable 接口,如下实现多线程类 MyRunnable :
public class MyRunnable implements Runnable { private String name; public MyRunnable(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; i++) { System.out.println("name:" + name + ",i=" + i); } } }
多线程操作类:
public class MyRunnableTest{ public static void main(String[] args) { MyRunnable thread1 = new MyRunnable("线程A"); MyRunnable thread2 = new MyRunnable("线程B"); new Thread(thread1).start(); new Thread(thread2).start(); } }
实现Runnable 接口实现的多线程类并没有 start() 方法,而是使用 Thread 类的代理来实现多线程的操作。Thread类中有一个类型为Runnable的属性为target,Thread类中的run()方法就是调用该 Runnable 属性的run()方法,如下:
private Runnable target; ...... public void run() { if (target != null) { target.run(); } }
继承Thread 类实现的多线程不能实现资源共享,而实现 Runnable 接口实现的多线程可以实现资源共享。 如下使用继承Thread 实现的多线程类 MyThread ,模拟卖票场景操作类变量 ticket :
public class MyThread extends Thread { private int ticket = 5; public void run() { for (int i = 0; i < 50; i++) { if (ticket > 0) { System.out.println("卖票:tichek=" + ticket--); } } } }多线程操作类为:
public class ThreadTicket { public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); MyThread thread3 = new MyThread(); thread1.start(); thread2.start(); thread3.start(); } }程序输出为:
卖票:tichek=5
卖票:tichek=4
卖票:tichek=3
卖票:tichek=5
卖票:tichek=4
卖票:tichek=5
卖票:tichek=4
卖票:tichek=3
卖票:tichek=2
卖票:tichek=1
卖票:tichek=3
卖票:tichek=2
卖票:tichek=1
卖票:tichek=2
卖票:tichek=1
发现每个线程都操作ticket 变量 5 次,而不是操作一个共享变量,因为这里实例化了3个MyThread对象,把 MyThread 的多线程实现改为实现 Runnable 接口,如:
public class ThreadTicket { public static void main(String[] args) { MyRunnable thread1 = new MyRunnable(); new Thread(thread1).start(); new Thread(thread1).start(); new Thread(thread1).start(); } }
输出为:
卖票:tichek=3
卖票:tichek=2
卖票:tichek=1
卖票:tichek=4
多线程中所有操作方法都是在Thread 类中定义的,如:
设置设置线程名称方法:
1、 使用set 方法: public final void setName(String name)
2、 构造方法:public Thread(Runnable target,String name),
public Thread(String name);
取得名字:public final String getName();
对于线程的名字,最好是在线程启动前进行设置,最好不要设置相同名字,也不要为一个线程改名字。
Thread类还提供一个静态方法来获取当前操作的线程: public static Thread currentThread();
如下线程实现类:
public class MyRunnable implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " 正在运行..."); } } }
线程操作类:
public class ThreadNameDemo { public static void main(String[] args) { MyRunnable myrunnable = new MyRunnable(); Thread thread1 = new Thread(myrunnable, "线程A"); Thread thread2 = new Thread(myrunnable, "线程B"); Thread thread3 = new Thread(myrunnable, "线程C"); thread1.start(); thread2.start(); thread3.start(); } }
运行输出:
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程B 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
线程C 正在运行...
把线程实现类改成:
public class ThreadNameDemo { public static void main(String[] args) { MyRunnable myrunnable = new MyRunnable(); Thread thread1 = new Thread(myrunnable, "线程A"); thread1.start(); myrunnable.run(); } }
运行输出:
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
main 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
线程A 正在运行...
说明在程序运行时,主方法就是一个主 mail线程。
Thread类的 sleep() 方法实现线程的休眠,如下线程实现类:
public class MyRunnable implements Runnable { public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO : handle exception } System.out.println(Thread.currentThread().getName() + " 正在运行..."); } } }
线程操作类为:
public class ThreadSleepDemo { public static void main(String[] arge) { MyRunnable myrunnable = new MyRunnable(); new Thread(myrunnable, "线程A").start(); new Thread(myrunnable, "线程B").start(); } }
运行结果为:
线程B 正在运行...
线程A 正在运行...
线程B 正在运行...
线程A 正在运行...
线程B 正在运行...
线程A 正在运行...
线程B 正在运行...
线程A 正在运行...
线程B 正在运行...
线程A 正在运行...
Thread类提供 interrupt() 类提供中断线程的执行。
Thread类提供 public final void setPriority ( int newPriority) 方法设置线程优先级,线程优先级有下面三种:
最高: MAX_PRIORITY 10
中等: NORM_PRIORITY 5
最低: MIN_PRIORITY 1
Main线程的优先级为 5
线程同步
使用上面模拟卖票程序,如果卖票程序中加入延迟,则运行的效果跟之前的不一样,如:
public class MyRunnable implements Runnable { private int ticket = 5; public void run() { for (int i = 0; i < 50; i++) { if (ticket > 0) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("卖票:tichek=" + ticket--); } } } }
线程操作类不变:
public class ThreadTicket { public static void main(String[] args) { MyRunnable myrunnable = new MyRunnable(); new Thread(myrunnable).start(); new Thread(myrunnable).start(); new Thread(myrunnable).start(); } }
运行结果为:
卖票:tichek=5
卖票:tichek=3
卖票:tichek=4
卖票:tichek=2
卖票:tichek=1
卖票:tichek=0
卖票:tichek=-1
在 java中可以通过同步代码进行代码的加锁操作,有两种实现方法:
1、 同步代码块
使用synchronized 关键字进行同步代码块声明,但是在此操作时必须要明确指出到底锁定的是哪个对象,一般都是以当前对象 this 为主,如上面模拟卖票代码,加上同步后为:
public class MyRunnable implements Runnable { private int ticket = 5; public void run() { for (int i = 0; i < 50; i++) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("卖票:tichek=" + ticket--); } } } } }此时输出正常,但是输出速度明显变慢。
2、 同步方法
模拟上面卖票程序,改用同步方法实现为:
public class MyRunnable implements Runnable { private int ticket = 5; public void run() { for (int i = 0; i < 50; i++) { sale(); } } public synchronized void sale() { if (ticket > 0) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("卖票:tichek=" + ticket--); } } }
在程序中过多的同步为出现死锁,关于Java中锁机制和synchronized关键字看下面两篇文章:
http://chenzehe.iteye.com/blog/1741046
http://chenzehe.iteye.com/blog/1766790
生产者和浪费者,生产者不断生产内容,消费者不断取出内容,如下基础类Info ,生产者和消费者不断对其进行操作:
public class Info { private String country; private String city; public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } //生产者,不断的对Info 对象设值: public class Product implements Runnable { private Info info = null; public Product(Info info) { this.info = info; } public void run() { for (int i = 0; i < 10; i++) { if (i % 2 == 1) { info.setCountry("中国"); try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } info.setCity("杭州"); } else { info.setCountry("美国"); try{ Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } info.setCity("硅谷"); } } } } //消费者,不断的对 Info对象取值 : public class Customer implements Runnable { private Info info = null; public Customer(Info info) { this.info = info; } public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("country:" + info.getCountry() + "->city:" + info.getCity()); } } } //多线程操作类: public class ProCusDemo { public static void main(String[] args) { Info info = new Info(); Product product = new Product(info); Customer customer = new Customer(info); new Thread(product).start(); new Thread(customer).start(); } }
运行输出为:
country:中国->city:硅谷
country:美国->city:杭州
country:中国->city:硅谷
country:美国->city:杭州
country:中国->city:硅谷
country:美国->city:杭州
country:中国->city:硅谷
country:美国->city:杭州
country:中国->city:硅谷
country:中国->city:杭州
上面代码出现了生产的内容有可能不一至的情况,还出现了重复取值和重复设值的情况,为了保证数据的完整性,一般加入同步操作,如 Info类加入同步方法后为:
public class Info { private String country; private String city; public synchronized void set(String country, String city) { this.country = country; try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.city = city; } public synchronized void get() { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("country:" + this.country + "->city:" + this.city); } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } 生产者为: public class Product implements Runnable { private Info info = null; public Product(Info info) { this.info = info; } public void run() { for (int i = 0; i < 10; i++) { if (i % 2 == 1) { info.set("中国", "杭州"); } else { info.set("美国", "硅谷"); } } } } 消费者为: public class Customer implements Runnable { private Info info = null; public Customer(Info info) { this.info = info; } public void run() { for (int i = 0; i < 10; i++) { info.get(); } } }多线程操作类不变,输出为:
country:美国->city:硅谷
country:中国->city:杭州
country:中国->city:杭州
country:中国->city:杭州
country:中国->city:杭州
country:中国->city:杭州
country:中国->city:杭州
country:中国->city:杭州
country:美国->city:硅谷
country:中国->city:杭州
此时能够保证代码的一至性,但还是存在重复取和重复设置问题,可以使用Object 类的 wait() 方法和 notiry() 方法进行修改,如: Info类为:
public class Info { private String country; private String city; private boolean flag = true; // true表示可以生产,但不能消费,false表示可以消费,但不能生产 public synchronized void set(String country, String city) { if (!flag) { try { super.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.country = country; try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.city = city; this.flag = false; super.notify(); } public synchronized void get() { if (flag) { try { super.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("country:" + this.country + "->city:" + this.city); this.flag=true; super.notify(); } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
其它类不变,运行结果为:
country:美国->city:硅谷
country:中国->city:杭州
country:美国->city:硅谷
country:中国->city:杭州
country:美国->city:硅谷
country:中国->city:杭州
country:美国->city:硅谷
country:中国->city:杭州
country:美国->city:硅谷
country:中国->city:杭州
相关推荐
这个"JAVA多线程基础演练DEMO"提供了一些基础示例,帮助开发者更好地理解和掌握多线程的基本概念和用法。以下将详细讲解Java多线程的相关知识点: 1. **线程的创建** - **继承Thread类**:创建一个新类,继承自`...
Java多线程是Java编程中的重要...以上内容仅涵盖了Java多线程基础编程的一部分知识点,实际开发中还需要关注更多的并发控制策略、性能优化和调试技巧。对于深入理解Java多线程,还需要学习和实践更多相关的高级特性。
Java多线程是Java编程中的一个...以上只是Java多线程基础知识的一部分,深入学习还包括线程池的配置与优化、线程安全的设计模式、并发工具类的使用等。理解和掌握这些知识点对于编写高效、稳定的多线程程序至关重要。
java多线程基础说课PPT教案.pptx
Java多线程基础篇讲解是针对初学者设计的教程,旨在用简洁明了的语言帮助学习者更容易理解多线程的概念和应用。多线程编程在现代计算机系统中扮演着重要角色,尤其在CPU主频发展遇到瓶颈的情况下,通过超线程技术和...
Java多线程基础知识 Java多线程基础知识是Java编程语言中的一项重要技术,用于提高程序的执行效率和响应速度。在这里,我们将详细介绍Java多线程基础知识的相关概念和技术。 一、程序、进程和线程 程序(Program...
本资料“Java多线程基础-01、数组概述”将带你入门Java的多线程世界,并结合数组这一基本数据结构进行讲解。 首先,我们需要理解什么是线程。线程是操作系统分配CPU时间的基本单位,一个进程可以有多个线程,它们...
以上就是Java多线程基础学习的一些关键知识点,理解并掌握这些内容对于进行高效的多线程编程至关重要。在实际开发中,还需要根据具体需求选择合适的线程模型和同步机制,以实现高效、安全的并发程序。
Java 多线程基础笔记
总之,Java多线程基础是高效编程的关键,而理解数组定义和元素默认值规则则是构建安全、健壮的多线程程序的基础。通过合理运用多线程技术和掌握数组的内在特性,你可以编写出更加高性能、可扩展的Java应用程序。
在Java编程语言中,多线程是程序设计中的一个重要概念,尤其在处理高并发和资源优化的场景下...通过观看"Java多线程基础-02、数组定义方式一、访问、注意事项.mp4"这个视频教程,你将能更深入地学习这些重要的知识点。
java多线程基础说课学习课程.pptx
java多线程基础说课学习教案.pptx
java多线程基础说课PPT学习教案.pptx
一、Java多线程基础 1. 线程的创建:Java提供了两种创建线程的方式——继承Thread类和实现Runnable接口。继承Thread类可以直接创建一个新的线程类,而实现Runnable接口则可以将线程逻辑封装在任何类中,更利于代码...
一、Java多线程基础 1. 创建线程: - 继承Thread类:创建一个新的类,继承自Thread类,重写其run()方法,然后创建该类的实例并调用start()方法。 - 实现Runnable接口:创建一个实现了Runnable接口的类,实现run()...
#### 二、Java多线程基础 ##### 1. 线程的概念 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。一个标准的解释是:“线程是一种轻量级进程,它是进程内的一个执行实体。”Java中的每个程序至少...
### Java多线程基础 #### 线程的生命周期 Java线程有多种状态,主要包括: 1. **New(新建)**:当使用`new`关键字创建一个新的线程对象但尚未调用`start()`方法时,线程处于New状态。 2. **Runnable(可运行)**...
1. **Java多线程基础知识**:Java多线程允许程序同时执行多个任务,这是通过创建和管理线程实现的。Java提供了两种创建线程的方式:继承Thread类和实现Runnable接口。线程的状态包括新建、就绪、运行、阻塞和终止,...
#### 二、Java多线程基础 1. **线程创建**: - 继承`Thread`类。 - 实现`Runnable`接口。 2. **线程调度**: - 线程调度是由操作系统完成的,Java虚拟机通过与操作系统交互来管理线程的执行。 - Java中线程...