java 多线程学习
一、概念
程序、进程、线程
程序 是计算机指令的集合。
进程 是一个运行中的程序,它指的是从代码加载,执行到执行结束这样一个完整过程。每个进程占用不同的内存空间。
线程 是进程中某个单一顺序的控制流,它被操作系统调度,并在处理器或内核上运行。同一个进程的多个线程共享一个内存空间。
二、线程的生命周期
概念
线程生命周期有五种状态


示例代码
三、线程的作用
多线程是为了使多个线程并行工作以完成多项任务,来提高系统的效率。
CPU在某一个时间点只能处理一个线程。cpu是通过时间片段的方式来分配CPU处理时间,通过多线程将程序划分成多个独立的任务,可以使CPU处理的效率大大提高。
总之一句话,提高程序运行效率。
四、实现多线程的方法
概念
实现多线程的方式有两种,一种是继承Thread(java.lang.Thread)类并重写run()方法,另一种是实现Runnable接口((java.lang.Runnable)。
示例代码
public class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("线程正在运行!!!");
}
public static void main(String[] args) {
ThreadTest thread1=new ThreadTest();
ThreadTest thread2=new ThreadTest();
ThreadTest thread3=new ThreadTest();
thread1.start();
thread2.start();
thread3.start();
}
}
public class RunnableTest implements Runnable {
public void run() {
System.out.println("线程正在运行!!!");
}
public static void main(String[] args) {
//第一种使用方式
RunnableTest runTest1=new RunnableTest();
RunnableTest runTest2=new RunnableTest();
RunnableTest runTest3=new RunnableTest();
Thread thread1=new Thread(runTest1);
Thread thread2=new Thread(runTest2);
Thread thread3=new Thread(runTest3);
thread1.start();
thread2.start();
thread3.start();
//第二种方式
RunnableTest runTest4=new RunnableTest();
Thread thread4=new Thread(runTest4);
Thread thread5=new Thread(runTest4);
Thread thread6=new Thread(runTest4);
thread4.start();
thread5.start();
thread6.start();
//其实以上两种没区别 ,建议使用第二种,代码简洁
}
}
五、多线程同步的问题
概念
多线程访问共享资源的不同步问题。
示例代码
经典的多窗口售票案例
package com.haiqin;
public class RunnableTest implements Runnable {
private int ticket=10;
public void run(){
while(true) {
if(ticket>0) {
try {
//是对当前线程操作么?
Thread.sleep(10);
//Thread.currentThread().sleep(10);
System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//第二种方式
RunnableTest runTest=new RunnableTest();
Thread thread1=new Thread(runTest,"窗口1");
Thread thread2=new Thread(runTest,"窗口2");
Thread thread3=new Thread(runTest,"窗口3");
thread1.start();
thread2.start();
thread3.start();
//其实以上两种没区别 ,建议使用第二种,代码简洁
}
}
运行结果

实现多线程同步有两种方式,一种是synchoronized 关键字 另一种是Lock方式
synchoronized 关键字
概念:java虚拟机通过给每个对象(类对象或者实例对象)加锁的方式来实现多线程的同步。java虚拟机通过锁来确保某一对象在任何同一时刻最多只有一个线程能够运行与该对象相关联的同步语句块和同步方法。
//同步语句块
synchronized(this){
//代码
} //同步方法
public synchronized void fun {
//代码
}
示例代码
同步语句块
public class RunnableTest implements Runnable {
private int ticket=10;
public void run(){
while(true) {
synchronized (this) {
if(ticket>0) {
try {
//是对当前线程操作么?
Thread.sleep(10);
//Thread.currentThread().sleep(10);
System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
public static void main(String[] args) {
//保证runTest这个实例唯一(单例模式),同步语句快中的this指的是调用这个语句块的对象,对这个对象上锁,如果对象不唯一,则同步语句块会失效。
RunnableTest runTest=new RunnableTest();
Thread thread1=new Thread(runTest,"窗口1");
Thread thread2=new Thread(runTest,"窗口2");
Thread thread3=new Thread(runTest,"窗口3");
thread1.start();
thread2.start();
thread3.start();
//其实以上两种没区别 ,建议使用第二种,代码简洁
}
}

同步方法
public class RunnableTest implements Runnable {
private int ticket=10;
public void run(){
while(ticket>0) {
shell();
}
}
public synchronized void shell() {
if(ticket>0) {
try {
//是对当前线程操作么?
Thread.sleep(10);
//Thread.currentThread().sleep(10);
System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//要保证 实例对象唯一
RunnableTest runTest=new RunnableTest();
Thread thread1=new Thread(runTest,"窗口1");
Thread thread2=new Thread(runTest,"窗口2");
Thread thread3=new Thread(runTest,"窗口3");
thread1.start();
thread2.start();
thread3.start();
//其实以上两种没区别 ,建议使用第二种,代码简洁
}
}

说白了就是 synchronized 锁的是对象不是代码。想要实现锁可以对象使用单例【锁对象】,或者Synchronized(xx.class)[这个是锁代码]
Lock方式(锁代码)
概念:
锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。(和synchronized意思一样,实现方式不同。)
示例代码
package com.haiqin; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class LockTest{ private int ticket=10; private Lock lock = new ReentrantLock(); public void shell(Thread thread){ if(lock.tryLock()){
try {
System.out.println("线程名"+thread.getName() + "获得了锁"); while(ticket>0) {
if(ticket>0) {
try {
//是对当前线程操作么?
//Thread.sleep(30);
Thread.currentThread().sleep(10);
System.out.println(thread.getName()+"正在售票----->"+(this.ticket--));
} catch (InterruptedException e) { e.printStackTrace();
}
}
} }catch(Exception e){ e.printStackTrace(); } finally { System.out.println("线程名"+thread.getName() + "释放了锁"); lock.unlock();
}
}else{ System.out.println("我是"+Thread.currentThread().getName()+"有人占着锁,我就不要啦");
}
} public static void main(String[] args) { final LockTest lockTest = new LockTest(); //线程1
Thread t1 = new Thread(new Runnable() { public void run() {
lockTest.shell(Thread.currentThread());
}
}, "窗口1"); //线程2
Thread t2 = new Thread(new Runnable() { public void run() {
lockTest.shell(Thread.currentThread());
}
}, "窗口2"); //线程3
Thread t3 = new Thread(new Runnable() { public void run() {
lockTest.shell(Thread.currentThread());
}
}, "窗口3"); t1.start();
t2.start();
t3.start(); } }
运行结果

Synchronized和Lock的区别

六、线程控制方法(略)
七、守护线程(后台进程)
概念:
在后台执行服务的线程。如java的垃圾自动回收线程等。
在一个进程中,只要所有前台进程已退出,那么后台线程会自动结束。
示例代码
package com.haiqin;
import java.io.IOException;
public class TestDaemon extends Thread{
public void run() {
for(int i=0;i<=100;i++) {
try {
Thread.sleep(100);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
TestDaemon testDaemon=new TestDaemon();
//线程变为守护线程
testDaemon.setDaemon(true);
testDaemon.start();
//随意按键盘,main线程结束
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
七、Thread类的源码和Runnable接口的源码
1 继承层次关系

2 源码
java.lang.Runnable 接口
public interface Runnable {
public abstract void run();
}
java.lang.Thread 实现类
八、Lock的类型(5种)

九、Lock的源码(继承层次图和常用类)
1 继承层次图

2 源码
java.util.concurrent.locks.Lock 接口
public interface Lock {
/**
* 获取锁。
* 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于阻塞状态(锁池里面)。
*/
void lock();
/**
* 获取锁。
* 如果获取锁了,就成功获取锁。如果没有获取锁,则中断(等待)。
*/
void lockInterruptibly() throws InterruptedException;
/**
* 获取锁。仅在调用时锁为空闲状态才获取该锁。
* 如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
*/
boolean tryLock();
/**
* 获取锁。
* 如果没有获取锁,先等待一定时间(阻塞),过了这个时间,则不继续等待。
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁。
*/
void unlock();
/**
* 返回绑定到此 Lock 实例的新 Condition 实例。
* 在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。
*/
Condition newCondition();
}
java.util.concurrent.locks.ReentrantLock 实现类
package java.util.concurrent.locks;
public class ReentrantLock implements Lock, java.io.Serializable {
/**
*
*/
abstract static class Sync extends AbstractQueuedSynchronizer
/**
*
*/
static final class NonfairSync extends Sync
/**
*
*/
static final class FairSync extends Sync
/**
* 创建一个 ReentrantLock 的实例。这等同于使用 ReentrantLock(false)
*/
public ReentrantLock()
/**
* 创建一个具有给定公平策略的 ReentrantLock。(公平锁)
*/
public ReentrantLock(boolean fair)
/**
* 获取锁。 如果获取不到锁,阻塞状态(等待锁)
*/
public void lock()
/**
* 获取锁。如果获取不到锁,不等待。
*/
public void lockInterruptibly()
/**
* 获取锁,如果获取不到锁,不等待。不支持公平锁。
*/
public boolean tryLock()
/**
* 获取锁,如果获取不到锁,等待一定时间。支持公平锁。
*/
public boolean tryLock(long timeout, TimeUnit unit)
/**
* 试图释放此锁。
*/
public void unlock()
/**
* 返回用来与此 Lock 实例一起使用的 Condition 实例。
*/
public Condition newCondition()
/**
* 查询当前线程保持此锁的次数。 调试用
*/
public int getHoldCount()
/**
* 查询当前线程是否保持此锁。 调试用
*/
public boolean isHeldByCurrentThread()
/**
* 查询此锁是否由任意线程保持。此方法用于监视系统状态,不用于同步控制。
* 如果任意线程保持此锁,则返回 true;否则返回 false
*/
public boolean isLocked()
/**
* 如果此锁的公平设置为 true,则返回 true。
*/
public final boolean isFair()
/**
* 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
*/
protected Thread getOwner()
/**
* 查询是否有些线程正在等待获取此锁。
*/
public final boolean hasQueuedThreads()
/**
* 查询给定线程是否正在等待获取此锁。
* 如果给定线程已加入队列并且正在等待此锁,则返回 true
*/
public final boolean hasQueuedThread(Thread thread)
/**
* 返回正等待获取此锁的线程估计数。该值仅是估计的数字,
* 因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。此方法用于监视系统状态,不用于同步控制。
*/
public final int getQueueLength()
/**
* 返回一个 collection,它包含可能正等待获取此锁的线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,
* 所以返回的 collection 仅是尽力的估计值。
* 所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,以提供更多的监视设施。
*/
protected Collection<Thread> getQueuedThreads()
/**
* 查询是否有些线程正在等待与此锁有关的给定条件。
* 注意,因为随时可能发生超时和中断,所以返回 true 并不保证将来某个 signal 将唤醒线程。此方法主要用于监视系统状态。
* 如果有任何等待的线程,则返回 true
*/
public boolean hasWaiters(Condition condition)
/**
* 返回等待与此锁相关的给定条件的线程估计数。注意,因为随时可能发生超时和中断,
* 所以只能将估计值作为实际等待线程数的上边界。此方法用于监视系统状态,不用于同步控制。
*/
public int getWaitQueueLength(Condition condition)
/**
* 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,
* 所以返回 collection 的元素只是尽力的估计值。
* 所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,提供更多的条件监视设施。、
*/
protected Collection<Thread> getWaitingThreads(Condition condition)
}
java.util.concurrent.locks.ReadWriteLock 接口
public interface ReadWriteLock {
/**
* 返回用于读取操作的锁。
*/
Lock readLock();
/**
* 返回用于写入操作的锁。
*/
Lock writeLock();
}
java.util.concurrent.locks.ReentrantReadWriteLock 实现类
public class ReentrantReadWriteLock
//使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。
public ReentrantReadWriteLock() {
this(false);
}
/**
* 使用给定的公平策略创建一个新的 ReentrantReadWriteLock。
* 如果此锁应该使用公平排序策略,则该参数的值为 true
*/
public ReentrantReadWriteLock(boolean fair)
//返回用于写入操作的锁。
public ReentrantReadWriteLock.WriteLock writeLock()
//返回用于读取操作的锁。
public ReentrantReadWriteLock.ReadLock readLock()
/**
* 如果此锁将公平性设置为 ture,则返回 true。
*/
public final boolean isFair()
/**
* 返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null。
*/
protected Thread getOwner()
/**
* 查询为此锁保持的读取锁数量。此方法设计用于监视系统状态,而不是同步控制。
*/
public int getReadLockCount()
/**
* 查询是否某个线程保持了写入锁。此方法设计用于监视系统状态,而不是同步控制。
*/
public boolean isWriteLocked()
/**
* 查询当前线程是否保持了写入锁。
*/
public boolean isWriteLockedByCurrentThread()
/**
* 查询当前线程在此锁上保持的重入写入锁数量。对于与解除锁操作不匹配的每个锁操作,writer 线程都会为其保持一个锁。
*/
public int getWriteHoldCount()
/**
* 查询当前线程在此锁上保持的重入读取锁数量。对于与解除锁操作不匹配的每个锁操作,reader 线程都会为其保持一个锁
*/
public int getReadHoldCount()
/**
* 返回一个 collection,它包含可能正在等待获取写入锁的线程。
*/
protected Collection<Thread> getQueuedWriterThreads()
/**
* 返回一个 collection,它包含可能正在等待获取读取锁的线程。
*/
protected Collection<Thread> getQueuedReaderThreads()
/**
* 如果有其他线程正等待获取锁,则返回 true
*/
public final boolean hasQueuedThreads()
/**
* 如果将给定的线程加入等待此锁的队列,则返回 true
*/
public final boolean hasQueuedThread(Thread thread)
/**
* 返回等待获取读取或写入锁的线程估计数目。
*/
public final int getQueueLength()
/**
* 返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。
*/
protected Collection<Thread> getQueuedThreads()
/**
* 查询是否有些线程正在等待与写入锁有关的给定条件。
*/
public boolean hasWaiters(Condition condition)
/**
* 返回正等待与写入锁相关的给定条件的线程估计数目。
*/
public int getWaitQueueLength(Condition condition)
/**
* 返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。
*/
protected Collection<Thread> getWaitingThreads(Condition condition)
}
java 多线程学习的更多相关文章
- Java多线程学习笔记
进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...
- Java多线程学习(转载)
Java多线程学习(转载) 时间:2015-03-14 13:53:14 阅读:137413 评论:4 收藏:3 [点我收藏+] 转载 :http://blog ...
- java多线程学习笔记——详细
一.线程类 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...
- 【转】Java多线程学习
来源:http://www.cnblogs.com/samzeng/p/3546084.html Java多线程学习总结--线程概述及创建线程的方式(1) 在Java开发中,多线程是很常用的,用得好的 ...
- JAVA多线程学习笔记(1)
JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...
- Java多线程学习(六)Lock锁的使用
系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...
- Java多线程学习(五)线程间通信知识点补充
系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...
- Java多线程学习(四)等待/通知(wait/notify)机制
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java多线程学习(三)volatile关键字
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java多线程学习(二)synchronized关键字(2)
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
随机推荐
- 解决Arcgis相同投影仍出现偏差的问题
网上下载了一个土地利用分类的polygon矢量图---导入arcgis--投影不是我想要的.出现了一些偏差 立即转换投影----按照网上教程--先定义投影-再定义坐标系---结果很糟糕,inconsi ...
- 集群容器管理之swarm ---服务管理
服务管理 # 创建服务docker service create --replicas 1 --name hello busybox # docker service update --args &q ...
- 【读书笔记】《Linux内核设计与实现》进程管理与进程调度
大学跟老师做嵌入式项目,写过I2C的设备驱动,但对Linux内核的了解也仅限于此.Android系统许多导致root的漏洞都是内核中的,研究起来很有趣,但看相关的分析文章总感觉隔着一层窗户纸,不能完全 ...
- php之异常处理
<?php declare(strict_types = 1); function demo(int $v):int{ return 1; } try{ demo("1"); ...
- Lua IDE工具-Intellij IDEA+lua插件配置教程(Chianr出品)
Lua 编译工具IDE-Intellij IDEA 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Ch ...
- Javascript 2.4
---恢复内容开始--- 弱类型语言,可以随意修改变量的数据类型 "..."中的字符串包含 " 时需要转义字符 \" '...'中的而字符串包含 ' 时需要转 ...
- kolla单节点部署openstack
virtualbox环境: 双网卡:enp0s3(桥接) 192.168.102.194 enp0s8(桥接) 无ip 块存储 50G 关闭防火墙,selinux. 配置yum源:wget ...
- Javascript var 和 let 的区别
Javascript var 和 let 的区别 var 是函数块的全局变量. let 是代码块的局部变量. let 变量不会提升,如果先使用后定义会 undefind. 参考: https://de ...
- OpenLDAP主从
yum -y install compat-openldap必须得安装这个 1:在主上 备份 cp /etc/openldap/slapd.conf /etc/open ...
- Maya中输出alembic文件的方法
Maya中输出alembic文件是有现成api调用的,与maya中大部分api一样,这个功能参数的传入是非常类似mel的,本质上讲都是kwargs类型的参数,所以我们传入的参数就需要整理成类似于mel ...