Java线程总结---第一天
线程和进程各自有什么区别和优劣:
进程是资源分配的最小单位,线程是程序执行的最小单位
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多,线程的上下文切换的性能消耗要小于进程。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据。
多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
并行与并发
并发是没有时间上的重叠的,两个任务是交替执行的,由于切换的非常快,对于外界调用者来说相当于同一时刻多个任务一起执行了;而并行可以看到时间上是由重叠的,也就是说并行才是真正意义上的同一时刻可以有多个任务同时执行。
小程序demo
public class Demo extends Thread {
@Override
public void run() {
while (true){
System.out.println(this.currentThread().getName());
}
}
public static void main(String[] args) {
Demo demo= new Demo();
demo.start();
}
}
public class Demo implements Runnable {
@Override
public void run() {
while (true){
System.out.println("hello thread");
}
}
public static void main(String[] args) {
Demo demo= new Demo();
Thread thread=new Thread(demo,"t1");
thread.start();
}
}
public class Demo{
public int count = 0;
public void print() {
while (true){
System.out.println(count++);
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
new Demo().print();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
new Demo().print();
}
}).start();
}
}
synchronized锁范围
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的class对象
同步方法块,锁是括号里面的对象
public class Demo{
public synchronized void synsMethod1(){
System.out.println("method1---");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synsMethod3();
}
public synchronized void synsMethod2(){
System.out.println("method2---");
}
public synchronized void synsMethod3(){
System.out.println(www.yaxingyule.cn"method3---");
}
public static void main(String[] args) {
Demo demo=new Demo( www.yingxionghui1.cn);
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run(www.tiaotiaoylzc.com) {
demo.synsMethod2();
}
}).start();
}
}
输出结果
method1---
method3---
method2---
再如下
public class Demo{
public synchronized void synsMethod1(){
System.out.println("method1---");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(www.mytxyl1.com);
}
synsMethod3();
}
public synchronized www.dituyule.org static void synsMethod2(){
System.out.println("method2-www.yongshi123.cn--");
}
public synchronized void synsMethod3(){
System.out.println("method3---");
}
public static void main(String[] args) {
Demo demo=new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run(www.ouyi3pt1.cn) {
demo.synsMethod2();
}
}).start();
}
}
输出结果(其中一种可能)
method1---
method2---
method3---
所以说静态方法的锁和非静态方法的锁是不一样的
Synchronized锁重入
关键字Synchronized拥有锁重入的功能,也就是在使用Synchronized的时候, 当一个线程得到一个对象的锁后,在该锁里执行代码的时候可以再次请求该对象的锁 时可以再次得到该对象的锁。
当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。
一个简单的例子就是:在一个Synchronized修饰的方法或代码块的内部调用本 类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的。
/**
* @program: demo
* @description:
* @author: lee
* @create: 2019-02-25
**/
public class Demo{
public synchronized www.yigouyule2.cn void synsMethod1(){
System.out.println("method1---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synsMethod2();
}
public synchronized void synsMethod2(){
System.out.println("method2---");
}
public static void main(String[] args) {
Demo demo=new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod2();
}
}).start();
}
}
为什么要引入可重入锁这种机制哪?
假如有一个线程T获得了对象A的锁,那么该线程T如果在未释放前再次请求该对象的锁时,如果没有可重入锁的机制,是不会获取到锁的,这样的话就会出现死锁的情况,所以最大的作用是避免死锁。
volatile与synchronized的
volatile关键字的作用就是强制从公共堆栈中取得变量的值,而不是线程私有的数据栈中取得变量的值。
关键字volatile是线程同步的轻量级实现,性能比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,代码块等。
多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
volatile可以保证数据的可见性,但不可以保证原子性(不是线程安全的),而synchronized可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。
volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。
ThreadLocal
ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。
简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。
最典型的应用
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 采用ThreadLocal封装Connection
*
* @author Administrator
*
*/
public class ConnectionManager {
//定义ThreadLocal静态变量,确定存取类型为Connection
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
/**
* 得到Connection
* @return
*/
public static Connection getConnection() {
Connection conn = connectionHolder.get();
//如果在当前线程中没有绑定相应的Connection
if (conn == null) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@localhost:1521:bjpowern";
String username = "drp1";
String password = "drp1";
conn = DriverManager.getConnection(url, username, password);
//将Connection设置到ThreadLocal
connectionHolder.set(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
/**
* 关闭数据库连接方法
* @return
*/
public static void closeConnection() {
Connection conn = connectionHolder.get();
if (conn != null) {
try {
conn.close();
//从ThreadLocal中清除Connection
connectionHolder.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭数据库连接方法
* @return
*/
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement pstmt) {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet rs ) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 事务开启
* @return
*/
public static void beginTransaction(Connection conn) {
try {
if (conn != null) {
if (conn.getAutoCommit()) {
conn.setAutoCommit(false); //手动提交
}
}
}catch(SQLException e) {}
}
/**
* 事务提交
* @return
*/
public static void commitTransaction(Connection conn) {
try {
if (conn != null) {
if (!conn.getAutoCommit()) {
conn.commit();
}
}
}catch(SQLException e) {}
}
/**
* 事务回滚
* @return
*/
public static void rollbackTransaction(Connection conn) {
try {
if (conn != null) {
if (!conn.getAutoCommit()) {
conn.rollback();
}
}
}catch(SQLException e) {}
}
}
初始化默认值
public class ThreadlLocalDemo {
/**
* 初始化一个ThreadLocal对象,并且初始值设置为0
*/
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public int getNextNumber() {
threadLocal.set(threadLocal.get() + 1);
return threadLocal.get();
}
static class DemoThread extends Thread {
ThreadlLocalDemo threadlLocalDemo = null;
public DemoThread(ThreadlLocalDemo localDemo) {
this.threadlLocalDemo = localDemo;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) { // 每个线程打印 5个值
System.out.println("Thread:" + Thread.currentThread().getName()
+ ",threadlLocalDemo:" + threadlLocalDemo.getNextNumber());
}
}
}
public static void main(String[] args) {
ThreadlLocalDemo threadlLocalDemo = new ThreadlLocalDemo();
DemoThread demoThread = new DemoThread(threadlLocalDemo);
DemoThread demoThread2 = new DemoThread(threadlLocalDemo);
DemoThread demoThread3 = new DemoThread(threadlLocalDemo);
DemoThread demoThread4 = new DemoThread(threadlLocalDemo);
demoThread.start();
demoThread2.start();
demoThread3.start();
demoThread4.start();
}
}
ThreadLocal实现的原理
首先,我们来看一下ThreadLocal的set()方法,因为我们一般使用都是new完对象,就往里边set对象了
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
上面有个ThreadLocalMap,我们去看看这是什么?
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//....很长
}
通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储
我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!
如果该Map不存在,则初始化一个:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
如果map存在
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thread维护了ThreadLocalMap变量
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null
从上面又可以看出,ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中! 于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。
有了上面的基础,我们看get()方法就一点都不难理解了:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal原理总结
每个Thread维护着一个ThreadLocalMap的引用
ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
Java线程总结---第一天的更多相关文章
- Java线程入门第一篇
Java线程的状态有6种 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法. 2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running) ...
- java 线程池第一篇 之 ThreadPoolExcutor
一:什么是线程池? java 线程池是将大量的线程集中管理的类,包括对线程的创建,资源的管理,线程生命周期的管理.当系统中存在大量的异步任务的时候就考虑使用java线程池管理所有的线程.减少系统资源的 ...
- java线程学习第一天__低配版的卖面包机
package Thread;import javax.xml.bind.ValidationEvent;class snacks{ private int SaledSnacks=0; ...
- Java 线程--实现java.lang.Runnable接口实现线程
Java线程的第一种实现方式,主要分两步,第一步是继承java.lang.Thread; 第二步是重写run()方法.接下来我们来看Java线程的第二种实现方式,也是分为两步,第一步,写一个类实现ja ...
- Java线程的概念
1. 计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行:当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了. 缓 ...
- 第24章 java线程(3)-线程的生命周期
java线程(3)-线程的生命周期 1.两种生命周期流转图 ** 生命周期:**一个事物冲从出生的那一刻开始到最终死亡中间的过程 在事物的漫长的生命周期过程中,总会经历不同的状态(婴儿状态/青少年状态 ...
- 第23章 java线程通信——生产者/消费者模型案例
第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...
- Java线程新特征——Java并发库
一.线程池 Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定 ...
- Java线程间通信方式剖析——Java进阶(四)
原创文章,同步发自作者个人博客,转载请在文章开头处以超链接注明出处 http://www.jasongj.com/java/thread_communication/ CountDownLatch C ...
随机推荐
- vi 替换
在vi编辑器中,能够利用 :s命令能够实现字符串的替换.详细的使用方法例如以下: 1.:s/str1/str2/ 用字符串 str2 替换行中首次出现的字符串str1: 2.:s/str1/str2/ ...
- 2.3《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——重命名,复制,删除
最常用的文件操作除了将文件列出来外,就应该是重命名,复制,删除了.正如将文件列出来一样,大多数现代操作系统为这些任务提供了用户图形界面,但是在许多场景中,用命令行还是会更方便. 使用mv命令重命名一个 ...
- 20155227辜彦霖《基于Cortex-M4的UCOSIII的应用》课程设计个人报告
20155227辜彦霖<基于Cortex-M4的UCOSIII的应用>课程设计个人报告 一.个人贡献 参与课设题目讨论及完成全过程: 资料收集: 负责主要代码调试: 撰写小组结题报告. 二 ...
- 20155234 昝昕明《基于ARM实验箱的国密算法应用》课程设计个人报告
20155234 昝昕明<基于ARM实验箱的国密算法应用>课程设计个人报告 个人贡献 参与课设题目讨论及完成全过程: 资料收集: SM1算法及和ARM之间通信 负责串口代码调试: 协调完成 ...
- 20155321 《网络攻防》 Exp2 后门原理与实践
20155321 <网络攻防> Exp2 后门原理与实践 实验内容 例举你能想到的一个后门进入到你系统中的可能方式? 我觉得人们在平时上网的时候可能会无意识地点击到一些恶意的网站,这些网站 ...
- python3获取文件及文件夹大小
获取文件大小 os.path.getsize(file_path):file_path为文件路径 >>> import os >>> os.path.getsize ...
- 程序员大佬推荐的java学习路线
作为我的第一篇博客,我第一个想到的就是在校时就看到的这篇文章.并且在之后的时间里自己都反复观看过,有时候这不单单是一篇学习路线,也是审视自己技术能力的里程碑,和激励自己的鞭挞绳. 先来个书籍清单: & ...
- Scrapy框架中的CrawlSpider
小思考:如果想要通过爬虫程序去爬取”糗百“全站数据新闻数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模块递归回调parse方法). 方法二: ...
- CommandoVM-虚拟机映像文件 | VM打开直接用
呵呵!自从火眼发布了这个CommandoVM,想必大家应该都挺激动,然而实际操作一下,基本炸裂-- 因为并没有给类似于kali这种直接安装的现成镜像,而是要通过github的脚本去完全网络安装 实际操 ...
- 4星|《流量池》:Luckin Coffee营销操盘手经验谈
流量池:“急功近利”的流量布局.营销转化 作者是一线营销操盘手,全书是作者的经验总结,这样的作者在营销类图书中比较罕见,因此这本书非常有价值. 全书是写给巨头之外的企业营销人员看的,这样的企业的流量来 ...