java多线程--多线程基础小结
什么是线程?
在同一个进程中可以执行多个任务,每一个任务可以看做一个线程.
线程是程序的执行单元,执行路径,使程序使用cpu的最基本单位
一个进程如果只有一条执行路径,那么就是单线程的
一个进程如果有多个执行路径,那么就是多线程的
多线程的意义:
多进程的存在是为了提高CPU的利用率,多线程的存在,不是为了提高程序的执行速度,而是为了提高应用程序的使用率.
程序的执行其实都是在抢CPU的资源,即CPU的执行权.
多个进程在抢这个资源,而其中一个进程如果有多个执行路径,即多个线程,那么就有更高的几率抢到CPU的执行权
JVM的启动是单线程的还是多线程的?
是多线程的,启动jvm是就相当于启动了一个进程,接着该进程会创建一个主线程来调用main方法,而同时还有启动其他的线程,比如说垃圾回收的线程.
两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口.以第一种为例:
package com.wang.reflect;
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
//获取当前正在执行的线程的名称,相当于this.getName();
System.out.println(Thread.currentThread().getName()+"::"+i);
}
}
}
public class TestReflect {
public static void main(String[] args) throws Exception {
MyThread mThread1=new MyThread();
MyThread mThread2=new MyThread();
//设置线程的名字,也可以直接通过带参构造函数设置线程名称
mThread1.setName("张无忌:");
mThread2.setName("赵敏:");
mThread1.start();
mThread2.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"::"+i);
}
}
}
一般我们使用第二种方式实现多线程,实现Runnable接口的方式相比较继承Thread类的方式有两个好处:
1.避免由于java单继承带来的局限性.
2.适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据,有效分离,较好地体现了面向对象的思想.
线程调度:
线程有两种调度模型:
1.分式调度模型,所有的线程轮流使用CPU的使用权,平均分配每个线程占用的CPU时间片.
2.抢占式调度模型,优先让优先级高的线程先执行,如果优先级相同,那么随机选择一个线程,优先级高的线程获得CPU的执行权的几率大一些,并不是严格按照优先级由高到低执行.
java使用的是抢占式调度模型.
方法介绍:
获取当前线程对象的优先级,默认优先级为5,范围是1~10 | int getPriority() |
设置当前线程的优先级,范围1~10 | void setPriority() |
在指定毫秒内让正在运行的线程休眠(暂停执行) | static void sleep(1000) |
----------------------------------------------------- | ----------------------------------- |
等待当前线程执行结束,该方法一般紧随start()方法之后 | final void join() |
暂停当前正在执行的对象,执行其他线程,(礼让线程) | static void yield() |
将该线程标记为守护线程或用户线程。 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 |
final void setDaemon(true) |
看这张图,来更好的理解守护线程:
假设现在有三个线程分别叫关羽,刘备,张飞.将关羽,张飞设置为守护线程,一旦刘备这个线程执行完毕(死掉),那么关羽,张飞也就没有活着的意义了(不再执行,退出).
中断线程,把线程状态终止,并抛出一个InterruptedException |
void interrupt() |
线程的生命周期:
案例练习:
电影院卖票,假设有三个窗口同时卖票,共有100张票,用多线程模拟这个过程(要解决的关键问题是:线程同步).
package com.wang.reflect;class SellTicket implements Runnable {// 共享变量private int tickets = 100;// 创建锁对象private Object obj = new Object();@Overridepublic void run() {while (true) {/*** 解决线程同步问题的 方案:* synchronized(锁对象){* //操作共享变量的代码* }**/synchronized (obj) {if (tickets > 0) {System.out.println(Thread.currentThread().getName()+ "正在出售第" + (tickets--) + "张票");}try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}public class SellTicketDemo {public static void main(String[] args) {SellTicket st = new SellTicket();Thread t1 = new Thread(st, "窗口1");Thread t2 = new Thread(st, "窗口2");Thread t3 = new Thread(st, "窗口3");t1.start();t2.start();t3.start();}}打印结果如下:
synchronized是一个关键字.可以加在一段代码外部,也可以放在一个方法前.它的锁对象可以是任意对象,当然也可以写成当前类的对象this.
- 同步代码块的锁对象:任意对象
- 同步方法的锁对象:当前对象this
- 同步静态方法的锁对象:当前类的字节码对象,类名.class;
java类库中线程安全的类有:StringBuffer,HashTable,Vector.
注意Vector是线程安全的,但是当我们需要一个线程安全的集合的时候,我们一般也不用它,在Collections类中获取线程安全的list集合的方法,假设我们现在需要获得一个线程安全的List<String>集合,可以这样写:
List<String> list=Collections.synchronizedList(new ArrayList<String>());
虽然说我们可以理解同步代码块和同步方法的锁对象问题,但我们并没有直接看到在哪儿加上锁又在哪儿释放锁,为了更清晰的表达如何加锁和释放锁,JDK1.5以后提供了一个新的锁对象Lock.下面通过Lock锁实现上述卖票的过程.
package com.wang.reflect;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class SellTicket implements Runnable {
// 共享变量
private int tickets = 100;
// Lock是一个接口,ReentrantLock是他的一个实现类
private Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
lock.unlock();//释放锁
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
线程同步有两个很明显的弊:一是效率低,二是容易产生死锁.
死锁问题:
两个或两个以上的线程,在争夺资源的过程中,发生的一种相互等待的过程.下面通过代码演示死锁问题的存在:
package com.wang.reflect;
class DieLock extends Thread{
private boolean flag;
public DieLock(boolean flag){
this.flag=flag;
}
public static final Object o1=new Object();
public static final Object o2=new Object();
@Override
public void run() {
if(flag){
synchronized(o1){
System.out.println("if o1");
synchronized (o2) {
System.out.println("if o2");
}
}
}else{
synchronized (o2) {
System.out.println("else o2");
synchronized (o1) {
System.out.println("else o1");
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1=new DieLock(true);
DieLock dl2=new DieLock(false);
dl1.start();
dl2.start();
}
}
细读代码,可以发现,理想状态下,应该会打印:
if o1
if o2
else o2
else o1
但是运行之后,发现打印结果大多为:
if o1 或者 else o2
else o2 if o1
其实这里就发生了线程互相等待,也就是死锁问题.
生产者-消费者模式描述:
package com.wang.reflect;
//学生实体类作为共享资源
class Student{
String name;//姓名
int age;//年龄
boolean flag;//标记变量,判断当前学生对象是否已创建赋值好
}
//模拟生产者线程类
class SetStudent implements Runnable{
//共享资源s
private Student s;
private int x=0;
public SetStudent(Student s) {
this.s=s;
}
@Override
public void run() {
while(true){
synchronized (s) {
if(s.flag){
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0){
s.name="郭靖";
s.age=24;
}else{
s.name="黄蓉";
s.age=18;
}
x++;
//生产完之后,将标记置为true,通知消费者来消费
s.flag=true;
//唤醒线程
s.notify();
}
}
}
}
//模拟消费者线程类
class GetStudent implements Runnable{
//共享资源s
private Student s;
public GetStudent(Student s) {
this.s=s;
}
@Override
public void run() {
while(true){
synchronized (s) {
if(!s.flag){
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name+":::"+s.age);
//消费完以后将标记之置于false,通知生产者来生产
s.flag=false;
//唤醒线程
s.notify();
}
}
}
}
//测试类
public class StudentDemo {
public static void main(String[] args) {
Student s =new Student();
SetStudent ss=new SetStudent(s);
GetStudent gs=new GetStudent(s);
Thread t1=new Thread(ss, "生产者");
Thread t2=new Thread(gs, "消费者");
t1.start();
t2.start();
}
}
在上面的代码中,生产者就是代表对Student属性进行赋值,消费者就是代表对Studnet属性进行打印,代码设置了等待唤醒机制.
上述代码可以优化的,可以讲生产者的赋值行为,和消费者的打印行为,封装到Student类中,写成两个同步方法.实现这两个功能.代码如下:
package com.wang.reflect;
//学生实体类作为共享资源
class Student {
private String name;// 姓名
private int age;// 年龄
boolean flag;// 标记变量,判断当前学生对象是否已创建赋值好
public synchronized void set(String name, int age) {
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.age = age;
this.flag = true;
this.notify();
}
public synchronized void get() {
if (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + ":::" + age);
this.flag = false;
this.notify();
}
}
// 模拟生产者线程类
class SetStudent implements Runnable {
// 共享资源s
private Student s;
private int x = 0;
public SetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
s.set("郭靖", 27);
} else {
s.set("黄蓉", 18);
}
x++;
}
}
}
// 模拟消费者线程类
class GetStudent implements Runnable {
// 共享资源s
private Student s;
public GetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
s.get();
}
}
}
// 测试类
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
SetStudent ss = new SetStudent(s);
GetStudent gs = new GetStudent(s);
Thread t1 = new Thread(ss, "生产者");
Thread t2 = new Thread(gs, "消费者");
t1.start();
t2.start();
}
}
java多线程--多线程基础小结的更多相关文章
- Java的多线程机制系列:(一)总述及基础概念
前言 这一系列多线程的文章,一方面是个人对Java现有的多线程机制的学习和记录,另一方面是希望能给不熟悉Java多线程机制.或有一定基础但理解还不够深的读者一个比较全面的介绍,旨在使读者对Java的多 ...
- Java多线程系列--“基础篇”11之 生产消费者问题
概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p ...
- Java多线程系列--“基础篇”04之 synchronized关键字
概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...
- Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式
概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...
- Java多线程系列--“基础篇”03之 Thread中start()和run()的区别
概要 Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start( ...
- Java多线程系列--“基础篇”05之 线程等待与唤醒
概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...
- Java多线程系列--“基础篇”06之 线程让步
概要 本章,会对Thread中的线程让步方法yield()进行介绍.涉及到的内容包括:1. yield()介绍2. yield()示例3. yield() 与 wait()的比较 转载请注明出处:ht ...
- Java多线程系列--“基础篇”07之 线程休眠
概要 本章,会对Thread中sleep()方法进行介绍.涉及到的内容包括:1. sleep()介绍2. sleep()示例3. sleep() 与 wait()的比较 转载请注明出处:http:// ...
- Java多线程系列--“基础篇”08之 join()
概要 本章,会对Thread中join()方法进行介绍.涉及到的内容包括:1. join()介绍2. join()源码分析(基于JDK1.7.0_40)3. join()示例 转载请注明出处:http ...
- Java多线程系列--“基础篇”09之 interrupt()和线程终止方式
概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于“阻塞状态”的线程2.2 终止处于“运行状态” ...
随机推荐
- tomcat:域名指向项目名
把项目部署到tomcat目录webapps后,使用域名访问的时候域名后面需要加上项目名称,如果想直接输入域名就可以访问项目呢?看了下tomcat官方文档,我修改了tomcat安装目录下conf/ser ...
- Android Support Library
title: Android Support Library tags: Support Library,支持库 grammar_cjkRuby: true --- DATE: 2016-5-13. ...
- iOS开发点滴:iPhone屏幕适配
最近开始做iOS开发,遇到一些小问题和解决方法,记录下. 今天是iPhone屏幕适配 iPhone5出来之后屏幕就有iPhone就有了2种尺寸:3.5寸和4寸,xcode 5 的IB设计器里面界面 ...
- OstrichNet 简易统计信息收集工具
Ostrich 是twitter用于监控服务器性能的一个scala库,项目地址https://github.com/twitter/ostrich, 主要功能是收集.展示统计信息, 同时也提供了关闭服 ...
- 求两点之间最短路径-Dijkstra算法
Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.D ...
- php性能优化
序 很长时间没有写博文了,最近换了工作,长时间加班,根本没有时间做其他事情!今天闲下来了,想一想php性能方面的事情.这也是我2014年的第一篇博文! 推荐阅读:初学者到中级者应该掌握的! p ...
- .NET 基础 一步步 一幕幕 [注释、命名规则、访问修饰符、数据类型、常量、变量]
注释.命名规则.访问修饰符.数据类型.常量.变量 话说一个不会写注释的程序猿的不是一个好吃货,我们本篇就从注释开始说起好了. 在C#中有三种注释: 第一种:单行注释 以//开头,后面的就是注释内容 ...
- Linux study
在centos5.5中编译LNMP环境 一.配置好ip, dns, 网关, 确保使用远程连接工具能够连接服务器 centos设置ip地址,网关, dns教程: http://www.osyumwei. ...
- JMeter--二、在Windows环境上搭建wordpress
为了学习使用JMeter,在Windows环境上搭建了wordpress. 使用JMeter录制或是编写登录worepress.编辑文章.删除文章的脚本. 首先了解一下wordpress是什么? Wo ...
- java线程 公平锁 ReentrantLock(boolean fair)
一.公平锁 1.为什么有公平锁 CPU在调度线程的时候是在等待队列里随机挑选一个线程,由于这种随机性所以是无法保证线程先到先得的(synchronized控制的锁就是这种非公平锁).但这样就会产生饥饿 ...