java多线程具体总结
一、Thread.start()与Thread.run()的差别
通过调用Thread类的start()方法来启动一个线程。这时此线程是处于就绪状态,并没有运行。然后通过此Thread类调用方法run()来完毕其运行操作的,这里方法run()称为线程体,它包括了要运行的这个线程的内容。Run方法运行结束,此线程终止,而CPU再运行其它线程。
而假设直接用Run方法。这仅仅是调用一个方法而已,程序中依旧仅仅有“主线程”这一个线程,并没有开辟新线程,其程序运行路径还是仅仅有一条,这样就没有达到写线程的目的。
測试代码例如以下
public void run() {
System.err.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.run();
t2.run();
}
}
输出结果为
>>当前进程为:main
>>当前进程为:main
改为用start方法:
public class MyThread implements Runnable {
public void run() {
System.err.println(">>当前进程为:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.start();
t2.start();
}
}
结果为:
>>当前进程为:Thread-1
>>当前进程为:Thread-2
二、ThreadLocal类具体解释
ThreadLocal非常easy让人望文生义,想当然地觉得是一个“本地线程”。事实上。ThreadLocal并非一个Thread,而是Thread的局部变量。或许把它命名为ThreadLocalVariable更easy让人理解一些。
当使用ThreadLocal维护变量时。ThreadLocal为每个使用该变量的线程提供独立的变量副本。所以每个线程都能够独立地改变自己的副本。而不会影响其它线程所相应的副本。
下面是线程局部变量(ThreadLocal variables)的关键点:
一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。
在多个线程操作该变量时候能够互不影响。由于每个线程操作的实际上是改变量的副本。ThreadLocal实例通常作为静态的私有的(private static)字段出如今一个类中。这个类用来关联线程。
当多个线程訪问ThreadLocal实例时。每个线程维护ThreadLocal提供的独立的变量副本。
下面是測试代码,用于測试:作用于一个对象上面的三个线程来操作同一个ThreadLoacl对象(integer 类型),看是否会出现脏读等现象:
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>();
public void run() {
num.set(0);
for (int i = 0; i < 3; i++) {
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName() + ":num="
+ num.get());
}
}
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test, "Thread-1");
Thread t2 = new Thread(test, "Thread-2");
Thread t3 = new Thread(test, "Thread-3");
t1.start();
t2.start();
t3.start();
}
}
运行结果例如以下:
Thread-3:num=1
Thread-2:num=1
Thread-1:num=1
Thread-2:num=2
Thread-3:num=2
Thread-2:num=3
Thread-1:num=2
Thread-1:num=3
Thread-3:num=3
从上面能够看出,全然没有出现脏读等的现象,因此ThreadLocal线程安全。
经常使用的使用:当DAO类作为一个单例类时,数据库链接(connection)被每个线程独立的维护。互不影响。
能够用来控制session的创建和使用,例如以下ThreadLocal<Session> session = new ThreadLocal<Session>();
ThreadLoacal与同步机制的比較:
1、在同步机制中,通过对象的锁机制保证同一时间仅仅有一个线程訪问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候须要锁定某个对象。什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
2、而ThreadLocal则从还有一个角度来解决多线程的并发訪问。ThreadLocal会为每个线程提供一个独立的变量副本。从而隔离了多个线程对数据的訪问冲突。由于每个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进ThreadLocal。
3、概括起来说,对于多线程资源共享的问题,同步机制採用了“以时间换空间”的方式,而ThreadLocal採用了“以空间换时间”的方式。前者仅提供一份变量。让不同的线程排队訪问,而后者为每个线程都提供了一份变量,因此能够同一时候訪问而互不影响。
三、InvalidMonitorStateException异常
调用wait()/notify()/notifyAll()中的不论什么一个方法时。假设当前线程没有获得该对象的锁,那么就会抛出IllegalMonitorStateException的异常(也就是说程序在没有运行对象的不论什么同步块或者同步方法时。仍然尝试调用wait()/notify()/notifyAll()时)。
由于该异常是RuntimeExcpetion的子类。所以该异常不一定要捕获(虽然你能够捕获仅仅要你愿意).作为RuntimeException。此类异常不会在wait(),notify(),notifyAll()的方法签名提及。
例如以下代码,划线的部分会发生该异常。由于没有对该对象运行同步操作。
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
this.wait(1000);
method1();
} else {
method2();
notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}
改为一下代码,划线部分:
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
synchronized(this){
this.wait(1000);
}
method1();
} else {
method2();
synchronized (this) {
notifyAll();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}
四、sleep()和wait()和suspend()的差别
差别一:
sleep是Thread类的方法。是线程用来控制自身流程的,比方有一个要报时的线程,每一秒中打印出一个时间。那么我就须要在print方法前面加上一个sleep让自己每隔一秒运行一次。就像个闹钟一样。
sleep() 指示当前线程暂停运行指定时间,把运行机会让给其它线程。可是监控状态依旧保持。到时后会自己主动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,用来线程间的通信,这种方法会使当前拥有该对象锁的线程等待。直到其它线程调用notify方法时再醒来。只是你也能够给它指定一个时间,自己主动醒来。这种方法主要是用在不同线程之间的调度。
对象调用wait()方法导致本线程放弃对象锁。进入等待此对象的等待锁定池。仅仅有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
差别二 :
调用wait方法会释放当前线程的锁,事实上线程间的通信是靠对象来管理的,全部操作一个对象的线程是这个对象通过自己的wait方法来管理的。就好像这个对象是电视机,三个人是三个线程,那么电视机的遥控器就是这个锁,假如如今A拿着遥控器,电视机调用wait方法,那么A就交出自己的遥控器,由jVM虚拟机调度,遥控器该交给谁。
调用sleep方法不会释放锁,由于sleep()是一个线程用于管理自己的方法。不涉及线程通信。还是上面的样例。假设A拿遥控器的期间,他能够用自己的sleep每隔十分钟调一次台。而在他调台歇息的十分钟期间,遥控器还在他的手上,其它人无法获得遥控器。
suspend() 方法easy发生死锁。
调用suspend()的时候。目标线程会停下来。但却仍然持有在这之前获得的锁。此时,其它不论什么线程都不能訪问锁定的资源,除非被"挂起"的线程恢复运行。对不论什么线程来说,假设它们想恢复目标线程,同一时候又试图使用不论什么一个锁定的资源,就会造成死锁
在下面情况下,持有锁的线程会释放锁:
1. 运行完同步代码块。
2. 在运行同步代码块的过程中。遇到异常而导致线程终止。
3. 在运行同步代码块的过程中,运行了锁所属对象的wait()方法,这个线程会释放锁,进行对象的等待池。
在下面情况下。线程虽然停止运行,可是线程不会释放锁:
1. 在运行同步代码块的过程中,运行了Thread.sleep()方法,当前线程放弃CPU,開始睡眠。在睡眠中不会释放锁。
2. 在运行同步代码块的过程中,运行了Thread.yield()方法,当前线程放弃CPU。但不会释放锁。
3. 在运行同步代码块的过程中,其它线程运行了当前对象的suspend()方法。当前线程被暂停。但不会释放锁。
五、在静态方法上使用同步
JAVA仅仅识别两种类型的锁:对象锁和类锁。
同步静态方法时会获取该类的"Class”对象。所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的锁。对整个类加锁,其它线程不能进入这个类的不论什么静态同步方法。它不像实例方法,由于多个线程能够同一时候訪问不同实例同步实例方法。測试代码例如以下:
public synchronized static void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized static void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
//下面代码创建了不同的对象上的不同线程。用来測试对于同一个类,会不会有锁
Common c1 = new Common();
Common c2 = new Common();
Thread t1 = new Thread(c1, "Thread-1");
Thread t2 = new Thread(c2, "Thread-2");
t1.start();
t2.start();
}
}
运行结果例如以下:
Running Thread-2
Running Thread-1
Method 2 called
Method 2 done
Method 1 called
Method 1 done
六、在一个对象上两个线程能够在同一时间分别调用两个不同的同步实例方法吗?
不能,由于一个对象已经同步了实例方法,线程获取了对象的对象锁。所以仅仅有运行完该方法释放对象锁后才干运行其它同步方法。
測试代码例如以下:
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
//下面代码作为对照,创建不同的对象,则就不会受对象锁的干扰了
//Common c1 = new Common();
//Common c2 = new Common();
//c1.start();
//c2.start();
}
}
运行结果例如以下:
Running Thread-1
Running Thread-2
Method 1 called
Method 1 done
Method 2 called
java多线程具体总结的更多相关文章
- 40个Java多线程问题总结
前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...
- Java多线程基础知识篇
这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程--让主线程等待子线程执行完毕
使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...
- Java多线程 2 线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
- java 多线程 1 线程 进程
Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报 分类: javaSE综合知识点(14) 版权声明:本文为博主原创文章,未经博 ...
- 一起阅读《Java多线程编程核心技术》
目录 第一章 Java多线程技能 (待续...)
- 第一章 Java多线程技能
1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...
- java从基础知识(十)java多线程(下)
首先介绍可见性.原子性.有序性.重排序这几个概念 原子性:即一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行. 可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到 ...
随机推荐
- luogu4012 深海机器人问题 网络流
关键词:最小费用最大流 题目大意:海底是个网格,每个网格边有一定价值的海底化石.每个路线可经过无限个机器人,但上面的化石只能采一次.机器人可沿网格边向东或向北移动.给定机器人起点和终点位置及所能容纳的 ...
- 软件开发 —— 极限编程(XP:Extreme Programming)
1. 软件开发的基本概念 软件开发的过程是:需求分析.设计.编码和测试. 2. 极限编程基本内涵 极限编程是一个轻量级的.灵巧的软件开发方法:同时它也是一个非常严谨和周密的方法. 它的基础和价值观是交 ...
- Java-Spring MVC:JAVA之常用的一些Spring MVC的路由写法以及参数传递方式
ylbtech-Java-Spring MVC:JAVA之常用的一些Spring MVC的路由写法以及参数传递方式 1.返回顶部 1. 常用的一些Spring MVC的路由写法以及参数传递方式. 这是 ...
- rehat7.X下postgresql 11编译安装
文档目录结构: 一.准备 操作系统版本:rehat7.6 Postgresql:11.2 软件安装目录:/pgsql11/basedir 数据文件存放目录:/pgsql11data/ 11.2的下载地 ...
- maven 打包jar && lib
一.springboot 打包成jar 1.pom.xml <build> <!-- jar的名称--> <finalName>shiro</finalNam ...
- 爬虫之 Requests库的基本使用
引入 Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用. 警告:非专业使用其他 HTTP 库会导致危险的副作用,包括:安全缺陷症.冗余代码症.重新发明轮子症.啃文档 ...
- Python笔记(九)
#encoding=utf-8 # python高级编程 # python面向对象 # 创建类 # 无意中把Visual Studio Code的窗口调小了,查了一下,可以使用Ctrl+=放大窗口,使 ...
- Android网络编程随想录(2)
上篇文章介绍了传输层TCP协议的理论知识,本文主要介绍了TCP协议基础之上HTTP协议和HTTPS协议的理论知识. HTTP协议基于TCP协议定义了客户端向服务器请求数据的方式,它是面向事务的应用层协 ...
- Poj Maya Calendar
http://poj.org/problem?id=1008 Maya Calendar Time Limit: 1000MS Memory Limit: 10000K Total Submissio ...
- How to share memory between services and user processes?
除了必要的InitializeSecurityDescriptor和SetSecurityDescriptorDacl, 内存映射文件名必须GLOBAL开头.