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 ...
随机推荐
- js中常见继承方式
1.原型模式 function Father(){ this.property = true; } Father.prototype.getValue = function(){ return thi ...
- 20155318 《网络攻防》Exp3 免杀原理与实践
20155318 <网络攻防>Exp3 免杀原理与实践 基础问题 杀软是如何检测出恶意代码的? 基于特征来检测:恶意代码中一般会有一段有较明显特征的代码也就是特征码,如果杀毒软件检测到有程 ...
- 20155327 李百乾 Exp7 网络欺诈防范
20155327 李百乾 Exp7 网络欺诈防范 基础问题回答 (1)通常在什么场景下容易受到DNS spoof攻击 就此次试验来看,被收到NDSspoof攻击,首先要被攻击机扫描,并被设置为目标,所 ...
- mfc CListCtrl
了解CListCtrl属性 了解CListCtrl常用成员函数 代码示例 一.CListCtrl常用属性 View:视图方式;.大(标准)图标2.小图标3.列表4.报表 Sort:排序; No Scr ...
- mfc Edit控件属性
设置Edit控件属性 窗口创建顺序 初始化Edit控件数据 一.设置Edit控件属性 .Align Text :Right 二.窗口创建顺序 CMywindowdlg: 窗口构造函数 OnCreate ...
- 蓝牙inquiry流程之Inquiry Complete处理
inquiry流程一般持续有12s多,当inquiry完成的时候,设备端会上报一个Event: Inquiry Complete 上来,那协议栈是如何把这个事件上传到应用层的呢?本篇文章来分析一下其具 ...
- 微信小程序云开发之云函数创建
云函数 云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写.一键上传部署即可运行后端代码. 小程序内提供了专门用于云函数调用的 API.开发者可以在云函数内使用 wx-server-sdk ...
- [UOJ#461]新年的Dog划分[二分图染色、二分]
题意 给你一张无向连通图,你并不知道有哪些边,你首先要回答这张图是否是二分图,如果是,回答这张图黑白染色过后的任意一个点集.你需要在2000次询问内找到结果,每次你可以询问原图中一个边集删掉后是否还连 ...
- 使用C#创建WCF服务控制台应用程序
本文属于原创,转载请注明出处,谢谢! 一.开发环境 操作系统:Windows 10 开发环境:VS2015 编程语言:C# IIS版本:10.0.0.0 二.添加WCF服务.Internet Info ...
- NAND Flash底层原理,SLC MLC TLC比较
NAND-Flash 的存储原理 固态硬盘最小单元的基本架构如下: 我们知道计算机中所有的信息储存最终都必须回归到 0与1,原则上,只要存储单元能提供两种或两种以上可供辨识的状态,便可以拿来纪录数据. ...