1、什么是进程?什么是线程?

进程概念:在计算机中运行的软件,是操作系统中最基础的组成部分 。进程是容器,里面装的都是线程。

线程概念:就是运行在进程中的一段代码,是进程中最小组织单元。

注意:

1、一个进程中至少要有一个线程,该线程必须是主线程

2、一个进程中可以有多个线程,至少要有一个主线程

3、多线程之间按照自己的执行路径同时运行(在cpu中交替执行),互不影响。

为什么多线程可以同时运行?

因为每个线程在CPU上执行的时间非常短,多线程执行的顺序高速的切换,是人类无法感受到的。

2、创建线程 的三种方式

创建线程方式一:继承Thread类 --------重写run()方法---------- 在主线程中启动(start())

注意:启动线程是调用start()而不是run(),  run()仅仅是线程的成员,不具有启动的功能。

创建线程方式二:定义 实现Runnable接口 实现类----------在类中重写run()-----------在主线程中先创建实现类的对象,传递给线程类对象的构造方法中

eg MyRunnable my = new MyRunnable();

Thread t1 = new Thread(my);

Thread t2 = new Thread(my);

创建线程方式三:实现Callable接口和Future创建线程--------重写call()方法,并且有返回值-----------在主线程中启动(start())

Callable接口是一个带泛型的接口,泛型的类型就是线程返回值的类型。实现Callable接口中的call()方法,方法的返回类型与泛型的类型相同。

Callable callable=new CallableThread();

FutureTask futureTask=new FutureTask(callable);

Thread thread=new Thread(futureTask);

thread.start();//开启一个线程

三种方式的区别:

1、实现 接口的方式可以避免java单继承带来的 局限性。

2、数据共享问题: 使用继承的方式 只能将属性修改为static,  而实现接口的方式可以自动做到数据共享(只创建一个Runnable接口实现类对象)3、 Callable规定(重写)的方法是call(),有返回值,并且可以可以抛出异常。Runnable规定(重写)的方法是run(),没有返回值,不能抛出异常。

4、运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

3、Thread类介绍

Thread类位于java.lang包下,常见的构造方法和字段如下:

Thread():无参的构造方法

Thread(Runnable target):依赖实现Runnable接口对象创建线程对象

Thread(String name):指定创建线程对象的名称

Thread(Runnable target,String name):指定创建线程对象的Runnable接口对象和名字

MAX_PRIORITY    =10;

MIN_PRIORITY    =1;

NORM_PRIORITY   =5;

这些字段给线程分配优先级,都有默认值。。

获取当前线程:Thread.currentThread()

获取线程的名字 :getName()

获取线程的ID:getId()

4、线程的优先级

概念: :如果一个线程的优先级越高,获得CPU资源的机会更大,不等于高优先级的就最先执行,

线程优先级的值为1-10,默认优先级为5。

如何设置优先级呢? 调用方法

线程对象.getPriority()                   返回线程的优先级

void setPriority(int newPriority)   更改线程的优先级

public static voidmain(String[] args) {

Thread main=Thread.currentThread();

System.out.println("主线程默认优先级:"+main.getPriority());//主线程默认优先级:5

main.setPriority(Thread.MAX_PRIORITY);

System.out.println("主线程修改后的优先级:"+main.getPriority());//主线程修改后的优先级:10

main.setPriority(7);

System.out.println("主线程修改后的优先级:"+main.getPriority());//主线程修改后的优先级:7

}

5、守护线程

守护线程(精灵线程/后台线程)

跟普通线程(用户线程)没区别

唯一区别是:当程序中没有普通线程时,守护线程也就没有用了,会自动死亡(死亡时间不确定),当程序中只剩下守护线程时,JVM可以关闭了特点

① 一般来说后台线程是为前台线程服务的(例如最典型的守护线程:gc线程,垃圾回收器);

② 如果所有的前台线程都死了,那么后台线程也会自动的死亡;但是前台线程死了,后台线程不一定立即死亡(可能还需要收尸...)

③ 一个线程的默认状态和创建它的环境线程状态一致(比如:已经设置一个线程为守护线程,那么在该线程里面创建的线程都是守护线程。)

常用方法:

setDaemon(true): 设置值为true是守护线程。

isDaemon():   判断当前线程是否是守护线程,返回   true 和 false.

注意:当一个线程处于运行状态的时候,不可以去改变守护状态,否则发生异常

6、线程终止 join()方法

join方法有三个重载版本:

某线程调用该方法,会让其他线程处于等待状态,让其运行完毕,再执行其他线程.

实际上调用join方法是调用了Object的wait方法wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。

对象名.join()

7、线程礼让yield()方法

Thread.yield()

调用此方法,当前线程会 让出cpu的使用权,优先执行其他线程,该线程也可能会处于被执行的对象。yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会更大而已。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

8、sleep()方法

sleep方法有两个重载版本:

sleep(long millis)//参数为毫秒

1. sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

2.  sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

3. 如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,

因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。

9、并行和并发的区别

并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,

再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。

并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,

两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

区别:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。

举个例子:

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。  (不一定是同时的)

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

10、解决线程安全问题的三种方法

线程安全:线程主体中的某段代码同时只有一个线程可以访问,其他线程处于等待状态

解决问题:为了解决多线程下访问资源共享时产生数据安全问题。

一、使用同步代码块

如:卖票案例 出现了线程安全 重复的票不能出现

步骤:

成员位置建立锁对象;

synchronized(锁对象){

出现安全问题代码

}

1、锁对象 任意对象

2、 必须保证多个线程使用的是同一个锁对象

3、把{} 只让一个线程进

例子:

public class RunnableImpl implements Runnable{

// 定义共享资源 线程不安全

private int ticket = 100;

//在成员位置创建一个锁对象

Object obj = new Object();

// 线程任务 卖票

@Override

public void run() {

while(true){

//建立锁对象

synchronized (obj){

if(ticket>0){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

//卖票操作

System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");

ticket--;

}

}

}

}

}

二、使用同步方法解决多线程安全

步骤

1 、创建一个方法 修饰符添加synchronized

2、 把访问了 共享数据的代码放入到方法中

3 、调用同步方法

public class RunnableImpl implements Runnable{

// 定义共享资源 线程不安全

private int ticket = 100;

// 线程任务 卖票

@Override

public void run() {

while(true){

payTicket();//调用下面synchronized修饰的方法

}

}

public synchronized void payTicket(){

if(ticket>0){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

//卖票操作

System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");

ticket--;

}

}

}

三、同步锁,不解锁会造成死锁

java.util.concurrent.locks.Lock;机制提供了比synchronized代码块和synchronized方法更广泛的锁操作,在类中定义一个锁对象: ReentrantLock lock = new ReentrantLock();

在实际业务代码中 先上锁 lock.lock() 执行完业务后 释放锁 lock.unlock()。

Lock lock = new ReentrantLock(true);

//加锁

lock.lock();

try{

//业务逻辑

}finally{

//放锁

lock.unlock();

}

11、线程的同步控制synchronized和lock的区别

1、用法:synchronized (隐式锁)在需要同步的对象中加入此控制,synchronized可以加在方法和代码块上面,括号中表示要锁的对象。

lock(显示锁)   需要显示在指定的 起始和终止位置 。 一般使用 ReentrantLock 类作为锁。 加锁 lock(),解锁 unlock() .   finally块一定要解锁,不然会造成死锁情况。

2、性能: synchronized 是Java的关键字,在JVM层面实现了对资源的同步互斥访问 .   而 lock 是一个接口。里面有许多方法。

比如:尝试获取锁包括立即返回是否成功的tryLock(),以及一直尝试获取的lock()方法和尝试等待指定时间长度获取的方法,比synchronized相对灵活了许多。

3、机制:synchronized(悲观锁CUP,怕别人抢吃的), 同一时刻不管是读还是写都只能有一个线程对共享资源操作,其他线程只能等待.  当锁的对象成功完成或者抛出异常时,会自动释放锁。

lock(乐观锁CAS) Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待。

lock():获取锁,如果锁被暂用则一直等待

unlock():释放锁

tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true

tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间

lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事

补充重点精华:ReentrentLock 和ReentrentReadWriteLock两个类可重入的锁,即当同一线程获取到锁之后,他在不释放锁的情况下,可以再次获取到当前已经拿到的锁,只需标记获取到锁的次数加一即可;

ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。

通过在读多,写少的高并发情况下,我们用ReentrantReadWriteLock分别获取读锁和写锁来提高系统的性能,因为读锁是共享锁,即可以同时有多个线程读取共享资源,而写锁则保证了对共享资源的修改只能是单线程的。

12、线程的生命周期

13、线程五种基本状态

新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。

根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

14、线程中的等待和通知机制

调用wait()方法之后,当前线程进入休眠状态  并  释放锁。

notify()  方法任意从 WAITTING 状态的线程中挑选一个进行通知(唤醒),使得调用wait()的方法的线程从等待队列移入到同步队列中,等待有机会再一次获取到锁,

调用通知后,当前线程不会马上释放该对象锁,要等到程序退出同步块后,当前线程才会释放锁。

notifyAll使所有原来在该对象上等待的线程统统退出WAITTING状态。

注意:   1.  如果调用wait()和 notify()  方法之前,线程必须要获得该对象的对象监视器锁,则会抛出IllegalMonitorStateException异常。

2.   实例化一个lock , 使用wait和notify的时候一定要配合synchronized关键字去使用 ,这都验证了 wait()和 notify()  方法 只能在同步方法或同步块中调用。

3. Condition的作用是对锁进行更精确的控制。

Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。

15、生产者和消费者模式

/* 资源类(资源池)

* 1.当消费者消费的资源不够时,通知生产者生产,消费者处于等待状态

* 2.当生产者生产的资源饱和时,通知消费者消费,生产者处于等待状态

*/

public class Resource {

private int num; //表示当前资源量

private int size = 20;//表示资源池的大小(饱和状态)

//表示消费了一个资源

public synchronized void remove(){

if(num>0){

num--;

System.out.println(Thread.currentThread().getName()+"消费了一件资源,剩余"+num+"件");        this.notify();

}else{

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public synchronized void add(){

if(num

num++;

System.out.println(Thread.currentThread().getName()+"生产了一件资源,剩余"+num+"件");

this.notify();//通知消费者消费

}else{

try {

this.wait();//当资源的数量处于饱和状态生产者处于等待状态

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

//消费者

public class CustomerRunnable implements Runnable {

Resource res;

public CustomerRunnable(Resource res){

this.res = res;

}

@Override

public void run() {

while(true){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

res.remove();

}

}

}

//生产者

public class ProducerRunnable implements Runnable{

Resource res;

public ProducerRunnable(Resource res){

this.res = res;

}

@Override

public void run() {

while(true){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

res.add();//表示让生产者生产

}

}

}

//测试类

public class Test {

public static void main(String[] args) {

Resource res = new Resource();//创建资源类

ProducerRunnable pr = new ProducerRunnable(res);//生产者

CustomerRunnable cr = new CustomerRunnable(res);//消费者

Thread pr1 = new Thread(pr,"生产者A");//生产者线程

Thread pr2 = new Thread(pr,"生产者B");

Thread cr1 = new Thread(cr,"消费者1号");//消费者线程

Thread cr2 = new Thread(cr,"消费者2号");

pr1.start();

pr2.start();

cr1.start();

cr2.start();

}

}

16、使用 juc锁 condition 来解决存钱和取钱案例

在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等。

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

public class Account {

private double money;//银行账户余额

private ReentrantLock lock = new ReentrantLock();

Condition con1 = lock.newCondition();

public double getMoney() {

return money;

}

public void setMoney(double money) {

this.money = money;

}

public void saveMoney(double money){

lock.lock();

if(this.money > 0){

try {

con1.await();//通知存钱线程处于等待状态

} catch (InterruptedException e) {

e.printStackTrace();

}

}else{

this.money+=money;

System.out.println("存钱:"+money);

}

con1.signal();//通知等待的取钱线程进行取钱

lock.unlock();

}

public void fetchMoney(double money){

lock.lock();

if(this.money > 0){

this.money-=money;

System.out.println("取钱"+money);

con1.signal();//通知等待的存钱线程进行存钱

}

try {

con1.await();//当账户没钱了,让取钱线程处于等待状态

} catch (InterruptedException e) {

e.printStackTrace();

}

lock.unlock();

}

}

public class FetchMoneyRunnable implements Runnable{

Account acc;//银行账户对象

public FetchMoneyRunnable(Account acc) {

this.acc = acc;

}

//模拟账户取钱10次

@Override

public void run() {

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

acc.fetchMoney(500);//取钱操作

}

}

}

public class SaveMoneyRunnable implements Runnable{

Account acc;

public SaveMoneyRunnable(Account acc) {

this.acc = acc;

}

// 模拟存钱10次

@Override

public void run() {

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

acc.saveMoney(1000);

}

}

}

public class Test {

public static void main(String[] args) {

Account acc = new Account();//创建一个账户

FetchMoneyRunnable fmr = new FetchMoneyRunnable(acc);

SaveMoneyRunnable smr = new SaveMoneyRunnable(acc);

Thread fetch = new Thread(fmr);//取钱线程

Thread save = new Thread(smr);//存钱线程

//启动线程

fetch.start();

save.start();

}

}

java 5 多线程_Java基础系列五、多线程相关推荐

  1. java代码讲解_Java基础系列-代码块详解

    注:本文出自博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 前言 Java基础系列,尽量采用通俗易懂.循序渐进的方式,让大家真正理解Java基础知识! 代码块 ...

  2. java throw 接口_Java基础系列-throw、throws关键字

    一.概述 throw和throws就是异常相关的关键字,在java中异常机制是一个非常重要的机制,我们需要重点掌握. 既然说到了异常,简单描述下异常机制很有必要,这也对后文的讲述提供前提. 二.Jav ...

  3. java必学_Java基础系列之初识JAVA

    工欲善其事必先利其器,在学习Java之初,大家一定要先了解下java的历史,以及找到一个适合自己的学习方法!很多同学在自学或者听课之初,完全不建议你直接使用一些IDE工具去写程序!自己先动手完全是很有 ...

  4. java 父类构造函数_Java基础系列 - 子类继承父类,调用父类的构造函数

    package com.test7; public class test7 { public static void main(String[] args) { Son son = new Son(1 ...

  5. java多线程教程_java 基础教程之多线程详解及简单实例

    java 多线程详解 在这篇文章里,我们关注多线程.多线程是一个复杂的话题,包含了很多内容,这篇文章主要关注线程的基本属性.如何创建线程.线程的状态切换以及线程通信. 线程是操作系统运行的基本单位,它 ...

  6. Java基础系列五 面向对象程序设计

    Java基础系列第一章 初识Java语言 Java基础系列第二章 Java语言基础 Java基础系列第三章 Java流程控制 Java基础系列第四章 Java数组与字符串 Java基础系列第五章 面向 ...

  7. java面试题 多线程_Java面试常见关于多线程的面试题

    多线程是Java技术中常用而且相对比较难易理解的一个知识点.而且多线程也是企业实际应用中必备的技术,因此在面试的过程中,面试者经常被问到关于多线程的问题,遇到这些问题大家应该怎么办呢?赶紧恶补一下Ja ...

  8. java nio多路复用_Java NIO系列教程(六) 多路复用器Selector

    多路复用器Selector是Java NIO编程的基础,熟练地掌握Selector对于掌握NIO编程至关重要.多路复用器提供选择已经就绪的任务的能力.简单来讲,Selector会不断地轮询注册在其上的 ...

  9. java 多线程语法_Java基础语法之多线程学习笔记整理

    众所周知,利用好多线程机制,可以大大提高系统整体的并发能力以及性能,而且线程间的切换和调度的成本小.因此,多线程是Java学习者必须掌握的语法重点.本文为大家整理了进程和线程.实现多线程方式.设置和获 ...

最新文章

  1. R语言使用ggplot2包的快速可视化函数qplot绘制分组箱图实战
  2. mysql router docker_Docker镜像瘦身 - SegmentFault 思否
  3. 未来计算机将怎么样改变我的生活,未来人类都不用上班 人工智能将如何改变我们的生活?...
  4. 【技术精华汇总】不一样的技术创新——阿里巴巴2016双11背后的技术
  5. springboot2.0集成activiti modeler
  6. 蔡高厅老师 - 高等数学-阅读笔记 - 01 - 前言、函数【视频第01、02、03、】
  7. 在web项目中集成xfire的方法
  8. python检测端口是否被侦听
  9. 显示器颜色偏灰不鲜明_你离学会后期调色,只差了一块颜色“正确”的摄影显示器...
  10. linux忘记mysql密码
  11. 指数函数在c语言中怎么输入,指数函数如果想得到整型的值怎样做?
  12. c语言贪吃蛇实训报告文档,C语言实现贪吃蛇.docx
  13. ElementUI表格表头对角线的绘制
  14. 计算机的基本组成和工作原理
  15. 进入bios看了,vt 已经开了,为什么打开模拟器还显示未开启?
  16. img标签,显示服务器图片
  17. 计算机思维在化工方面的应用,化工类院校计算机基础教学中计算思维的培养
  18. 跟Java面试官对线的一天!唬住就要50K,唬不住就要5K
  19. Git分支 查看branch 创建 切换checkout 合并merge(先切回主分支) 删除branch -d 推送push
  20. python模块 之 xlwt模块

热门文章

  1. 复杂的权限按钮控制优化
  2. 北亚数据恢复关于服务器存储MDisk重建的数据恢复方法的研究
  3. Innodb之监控Buffer pool Load progress
  4. Activity间数据传输
  5. Oracle 12c与GoldenGate 12c的一些问答
  6. CentOS 6.5下源码包安装配置JDK 7
  7. lamp软件包安装(rpm)
  8. Windows操作系统的发展历史
  9. IPV6 IPV4双栈互通与静态路由
  10. 麦咖啡Agent安装完后不合图标和不马上在EPO服务器上显示客户端的问题