可能是东半球最好的多线程讲义!

JAVA多线程

多线程的基本概念

线程指进程中的一个执行场景,也就是执行流程,那么进程和线程有什么区别呢?

  • 每个进程是一个应用程序,都有独立的内存空间
  • 同一个进程中的线程共享其进程中的内存和资源(共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的。)

什么是进程?

一个进程对应一个应用程序。例如:在 windows 操作系统启动 Word 就表示启动了一个
进程。在 java 的开发环境下启动 JVM,就表示启动了一个进程。现代的计算机都是支持多
进程的,在同一个操作系统中,可以同时启动多个进程。

多进程有什么作用?

单进程计算机只能做一件事情。
玩电脑,一边玩游戏(游戏进程)一边听音乐(音乐进程)。
对于单核计算机来讲,在同一个时间点上,游戏进程和音乐进程是同时在运行吗?不是。
因为计算机的 CPU 只能在某个时间点上做一件事。由于计算机将在“游戏进程”和“音乐
进程”之间频繁的切换执行,切换速度极高,人类感觉游戏和音乐在同时进行。
多进程的作用不是提高执行速度,而是提高 CPU 的使用率。
进程和进程之间的内存是独立的。

什么是线程?

线程是一个进程中的执行场景。一个进程可以启动多个线程。

多线程有什么作用?

多线程不是为了提高执行速度,而是提高应用程序的使用率。
线程和线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈。
可以给现实世界中的人类一种错觉:感觉多个线程在同时并发执行。

java 程序的运行原理?

java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,表示启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。

线程生命周期

线程是一个进程中的执行场景,一个进程可以启动多个线程

新建:采用 new 语句创建完成
就绪:执行 start 后
运行:占用 CPU 时间
阻塞:执行了 wait 语句、执行了 sleep 语句和等待某个对象锁,等待输入的场合
终止:退出 run()方法

多线程不是为了提高执行速度,而是提高应用程序的使用率.

线程和线程共享”堆内存和方法区内存”.栈内存是独立的,一个线程一个栈.

可以给现实世界中的人类一种错觉 : 感觉多线程在同时并发执行.

很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。

  • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
  • 并行与并发:

·

    • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

·

    • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:

     void transferMoney(User from, User to, float amount) {
 
      to.setMoney(to.getBalance() + amount);
 
      from.setMoney(from.getBalance() - amount);
     }

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程,该进程会自动启动一个”主线程”,

然后主线程去调用某个类的main()方法,所以main()方法运行在主线程中.

线程的调度与控制

线程的调度模型分为: 分时调度模型抢占式调度模型,Java使用抢占式调度模型
通常我们的计算机只有一个 CPU,CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。在单 CPU 的机器上线程不是并行运行的,只有在多个 CPU 上线程才可以并行运行。Java 虚拟机要负责线程的调度,取得 CPU 的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,Java 使用抢占式调度模型。分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

l  分时调度模型: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片

l  抢占式调度模型: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.

public class ThreadTest {

public static void main(String[] args) {

ThreadTest1();

// ThreadTest2();

// ThreadTest3();

// ThreadTest4();

// ThreadTest5();

}

/**

* 三个方法: 获取当前线程对象:Thread.currentThread(); 给线程起名: t1.setName("t1"); 获取线程的名字:

* t.getName();

*/

private static void ThreadTest1() {

Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"主线程"

System.out.println(t.getId());

Thread t1 = new Thread(new Processor1());

// 给线程起名

t1.setName("t1");

t1.start();

Thread t2 = new Thread(new Processor1());

t2.setName("t2");

t2.start();

}

/**

* 线程优先级高的获取的CPU时间片相对多一些 优先级: 1-10 最低: 1 最高: 10 默认: 5

*/

private static void ThreadTest2() {

Thread t1 = new Processor2();

Thread t2 = new Processor2();

t1.setName("t1");

t2.setName("t2");

System.out.println(t1.getPriority());

System.out.println(t2.getPriority());

t1.setPriority(1);

t2.setPriority(10);

t1.start();

t2.start();

}

/**

* 1.Thread.sleep(毫秒); 2.sleep方法是一个静态方法 3.该方法的作用: 阻塞当前线程,腾出CPU,让给其它线程

*/

private static void ThreadTest3() {

Thread t = new Thread(new Processor3());

t.start();

for (int i = 0; i < 11; i++) {

System.out.println(Thread.currentThread().getName() + "========>"

+ i);

try {

t.sleep(5000);// 等同于Thread.sleep(5000);阻塞的还是当前线程,和t线程无关.

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

/**

* 某线程正在休眠,如何打断它的休眠 以下方式依靠的是异常处理机制

*/

private static void ThreadTest4() {

try {

Thread t = new Thread(new Processor4());

t.start();

Thread.sleep(5000);// 睡5s

t.interrupt();// 打断Thread的睡眠

} catch (InterruptedException e) {

e.printStackTrace();

}

}

/**

* 如何正确的更好的终止一个正在执行的线程 需求:线程启动5s之后终止.

*/

private static void ThreadTest5() {

Processor5 p = new Processor5();

Thread t = new Thread(p);

t.start();

// 5s之后终止

try {

Thread.sleep(5000);

p.isRun = false;

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class Processor1 implements Runnable {

@Override

public void run() {

Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"t1线程对象"

System.out.println(t.getName());

System.out.println(t.getId());

}

}

class Processor2 extends Thread {

@Override

public void run() {

for (int i = 0; i < 50; i++) {

System.out.println(Thread.currentThread().getName()

+ "----------->" + i);

}

}

}

class Processor3 implements Runnable {

/**

* Thread中的run方法不能抛出异常,所以重写runn方法之后,在run方法的声明位置上不能使用throws

* 所以run方法中的异常只能try...catch...

*/

@Override

public void run() {

for (int i = 0; i < 11; i++) {

System.out.println(Thread.currentThread().getName() + "========>"

+ i);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Processor4 implements Runnable {

@Override

public void run() {

try {

Thread.sleep(1000000000);

System.out.println("能否执行这里");

} catch (InterruptedException e) {

e.printStackTrace();

}

for (int i = 0; i < 11; i++) {

System.out.println(Thread.currentThread().getName() + "========>"

+ i);

}

}

}

class Processor5 implements Runnable {

boolean isRun = true;

@Override

public void run() {

for (int i = 0; i < 11; i++) {

if (isRun) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

+ "========>" + i);

}

}

}

}

线程优先级

线 程 优 先 级 主 要 分 三 种 : MAX_PRIORITY( 最 高  );MIN_PRIORITY ( 最 低 级 )NORM_PRIORITY(标准)默认

//设置线程的优先级,线程启动后不能再次设置优先级
//必须在启动前设置优先级
//设置最高优先级
t1.setPriority(Thread.MAX_PRIORITY);

sleep

public class SleepTest {

public static void main(String[] args) {

System.out.println("Wait");

// 让主线程等待5秒再执行

Wait.bySec(5);

// 提示恢复执行

System.out.println("start");

}

}

class Wait {

public static void bySec(long s) {

// sleep s个1秒

for (int i = 0; i < s; i++) {

System.out.println(i + 1 + "秒");

try {

// sleep1秒

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

停止一个线程

l  如果我们的线程正在睡眠,可以采用 interrupt 进行中断

l  通常定义一个标记,来判断标记的状态停止线程的执行

yield

它与 sleep()类似,只是不能由用户指定暂停多长时间,并且 yield()方法只能让同优先级的线程有执行的机会,采用 yieid 可以将 CPU 的使用权让给同一个优先级的线程

public class YieldTest {

public static void main(String[] args) {

FirstThread mt = new FirstThread();

SecThread mnt = new SecThread();

mt.start();

mnt.start();

}

}

class FirstThread extends Thread{

public void run(){

for(int i=0;i<5;i++){

System.out.println("第一个线程的第 "+(i+1)+"次运行");

Thread.yield();  //暂停线程

}

}

}

class SecThread extends Thread{

public void run(){

for(int i=0;i<5;i++){

System.out.println("第二个线程的第 "+(i+1)+"次运行");

Thread.yield();

}

}

}

join

当前线程可以调用另一个线程的 join 方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行

public class JoinTest extends Thread {

public JoinTest(String name) {

super(name);

}

public void run() {

for (int i = 0; i < 5; i++)

System.out.println(getName() + "" + i);

}

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

if (i == 5) {

JoinTest tempjt = new JoinTest("半路加入的线程");

try {

tempjt.start();

tempjt.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName()+""+i);

}

}

}

 

synchronized

线程同步,指某一个时刻,指允许一个线程来访问共享资源,线程同步其实是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执行一个方法,采用线程同步解决以上的问题,我们只要保证线程一操作 s 时,线程 2 不允许操作即可,只有线程一使用完成 s 后,再让线程二来使用 s 变量

·         异步编程模型 : t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁.

·         同步编程模型 : t1线程和t2线程执行,t2线程必须等t1线程执行结束之后,t2线程才能执行,这是同步编程模型.

·

·         什么时候要用同步呢?为什么要引入线程同步呢?

·         1.为了数据的安全,尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制.

·         线程同步机制使程序变成了(等同)单线程.

·         2.什么条件下要使用线程同步?

·         第一: 必须是多线程环境

·         第二: 多线程环境共享同一个数据.

·         第三: 共享的数据涉及到修改操作.

 

// synchronized 是对对象加锁

// 采用 synchronized 同步最好只同步有线程安全的代码

// 可以优先考虑使用 synchronized 同步块

// 因为同步的代码越多,执行的时间就会越长,其他线程等待的时间就会越长

// 影响效率

public class TestWithdrawal {

public static void main(String[] args) {

// 创建两个线程

TestAccount r = new TestAccount();

Thread one = new Thread(r);

Thread two = new Thread(r);

one.setName("张三");

two.setName("张三的妻子");

// 启动线程

one.start();

two.start();

}

}

class Account {

private int balance = 500; // 余额

public int getBalance() {

return balance;

}

// 取款

public void withdraw(int amount) {

balance = balance - amount;

}

}

class TestAccount implements Runnable {

// 所有用TestAccount对象创建的线程共享同一个帐户对象

private Account acct = new Account();

public void run() {

for (int i = 0; i < 5; i++) {

makeWithdrawal(100);// 取款

if (acct.getBalance() < 0) {

System.out.println("账户透支了!");

}

}

}

private void makeWithdrawal(int amt) {

synchronized (acct) {

if (acct.getBalance() >= amt) {

System.out.println(Thread.currentThread().getName() + " 准备取款");

try {

Thread.sleep(500);// 0.5秒后实现取款

} catch (InterruptedException ex) {

}

// 如果余额足够,则取款

acct.withdraw(amt);

System.out.println(Thread.currentThread().getName() + " 完成取款,余额:"+acct.getBalance());

} else {

// 余额不足给出提示

System.out.println("余额不足以支付 "

+ Thread.currentThread().getName() + " 的取款,余额为 "

+ acct.getBalance());

}

}

}

}

 

死锁

public class DeadLock {

public static void main(String[] args) {

Object o1 = new Object();

Object o2 = new Object();

Thread t1 = new Thread(new T1(o1, o2));

Thread t2 = new Thread(new T2(o1, o2));

t1.start();

t2.start();

}

}

class T1 implements Runnable {

Object o1;

Object o2;

T1(Object o1, Object o2) {

this.o1 = o1;

this.o2 = o2;

}

@Override

public void run() {

synchronized (o1) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (o2) {

}

}

}

}

class T2 implements Runnable {

Object o1;

Object o2;

T2(Object o1, Object o2) {

this.o1 = o1;

this.o2 = o2;

}

@Override

public void run() {

synchronized (o2) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (o1) {

}

}

}

}

守护线程

从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。

·         其它所有的用户线程结束,则守护线程退出!

·         守护线程一般都是无限执行的.

public class DaemonThread {

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(new Runnable2());

t1.setName("t1");

// 将t1这个用户线程修改成守护线程.在线程没有启动时可以修改以下参数

t1.setDaemon(true);

t1.start();

// 主线程

for (int i = 0; i < 10; i++) {

System.out.println(Thread.currentThread().getName() + "----->" + i);

Thread.sleep(1000);

}

}

}

class Runnable2 implements Runnable {

@Override

public void run() {

int i = 0;

while (true) {

i++;

System.out.println(Thread.currentThread().getName() + "-------->"

+ i);

try {

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}
 
设置为守护线程后,当主线程结束后,守护线程并没有把所有的数据输出完就结束了,也即是说守护线程是为用户线程服务的,当用户线程全部结束,守护线程会自动结束
 

Timer.schedule()

/**

* 关于定时器的应用 作用: 每隔一段固定的时间执行一段代码

*/

public class TimerTest {

public static void main(String[] args) throws ParseException {

// 1.创建定时器

Timer t = new Timer();

// 2.指定定时任务

t.schedule(new LogTimerTask(), new SimpleDateFormat(

"yyyy-MM-dd HH:mm:ss SSS").parse("2017-06-29 14:24:00 000"),

10 * 1000);

}

}

// 指定任务

class LogTimerTask extends TimerTask {

@Override

public void run() {

System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS")

.format(new Date()));

}

}

我是melon,一个10年编程老司机。Q我3474203856。 给melon留言或者说明是看到文章过来的。所有相关技术问题都可以一起寻找到答案。

原文地址:http://blog.51cto.com/13884944/2155589

时间: 2024-08-29 19:37:46

可能是东半球最好的多线程讲义!的相关文章

Java多线程总结(一)多线程基础

本文转载地址:             http://www.cnblogs.com/zrtqsk/p/3776328.html 多线程是Java学习的非常重要的方面,是每个Java程序员必须掌握的基本技能.本文只是多线程细节.本质的总结,并无代码例子入门,不适合初学者理解.初学者学习多线程,建议一边看书.看博文,以便写代码尝试. 一.进程 进程是操作系统结构的基础:是一次程序的执行:是一个程序及其数据在处理机上顺序执行时所发生的活动.操作系统中,几乎所有运行中的任务对应一条进程(Process

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

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

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

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

多线程中sleep和wait的区别

前几天去UC笔试,有一道简答题问到了.之前还真一直没留意到这个问题,所以答得也不好. 无论学习什么都好,通过对比学习更有利于发现事物的共性和个性,对于知识点的理解更有明显效果(这也可能是UC笔试题上,5道简答题中,有4道都是关于X与Y的区别的问题的原因之一). 既然自己答得不好,那就写下这篇随笔,来警示下自己(不仅是sleep与wait区别,还有多用这种对比学习的学习方式). 翻了很多资料,说的最多的一句就是, sleep与wait最主要的区别在于,sleep与wait都可以使线程等待,但sle

学在前面——《疯狂JAVA讲义》学习笔记

一直放弃JAVA,觉得身为前端掌握html.css.js就可以胜任工作了,但是最近组里后台太忙了,有时候感觉只要在后台改一点东西就能满足前台所需要的数据了,但是自己却不会改,后台还木有空,于是觉得,嗯,身为一个IT的新新人才,当然也要回基本的JAVA了,不要求会写,但是基本的会改还是必要的~~ 于是看了各种JAVA从入门到精通的帖子,选中了<疯狂JAVA讲义>这本书,尽管书有点老了,但是感觉很全面,讲的很好,希望能够有所收获~俗话说,带着问题去学习,发现尽管很久以前了解过JAVA,但是前言里面

斯坦福 IOS讲义 课件总结 一

1,引入文件, #import <Foundation/Foundation.h> IOS7 中可以这样写 @import Foundation; 2,在.h文件引入的是公用的,在.m文件里面引用的是本类私有的.私有属性声明 @interface Card() //在这里面声明 @property (strong) NSString *contents; @end 3,strong 是保存对象在内存中,week相反,这两个词形容的是对象. 4,nonatomic,不涉及多线程,没有锁,所以快一

Android网络应用——疯狂android讲义笔记

基于TCP协议的网络通信 TCP协议基础 使用ServerSocket创建TCP服务器端 使用Socket进行通信 加入多线程 使用URL访问网络资源 使用URL读取网络资源 使用URLConnection提交请求 使用HTTP访问网络 HttpURLConnection类 使用Apache HttpClient类 使用webview视图显示网页 使用webvie浏览网页 用webview加载html代码 使用webview中的javaScript调用android方法 使用web servic

JAVA多线程和并发性知识点总结

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46861397 上次我总结了一份JAVA 面向对象和集合知识点总结: http://blog.csdn.net/zhoubin1992/article/details/46481759 受到了博友们的激励,这次对JAVA多线程和并发性相关知识点进行总结,方便各位博友学习以及自己复习之用. 一. 什么是进程.线程?线程和进程的区别? 1. 进程 当一个程序进入内存运行时,即变成一个进

《疯狂java讲义》读后感

<疯狂java讲义·第三版>,全书共851页,18章. 目录如下: 第1章 Java语言概述与开发环境 第2章 理解面向对象 第3章 数据类型和运算符 第4章 流程控制与数组 第5章 面向对象(上) 第6章 面向对象(下) 第7章 Java基础类库 第8章 Java集合 第9章 泛型 第10章 异常处理 第11章 AWT编程 第12章 Swing编程 第13章 MySQL数据库与JDBC编程 第14章 Annotation(注释) 第15章 输入/输出 第16章 多线程 第17章 网络编程 第