一、Thread.start()与Thread.run()的差别

通过调用Thread类的start()方法来启动一个线程。这时此线程是处于就绪状态,并没有运行。然后通过此Thread类调用方法run()来完毕其运行操作的,这里方法run()称为线程体,它包括了要运行的这个线程的内容。Run方法运行结束,此线程终止,而CPU再运行其它线程。

而假设直接用Run方法。这仅仅是调用一个方法而已,程序中依旧仅仅有“主线程”这一个线程,并没有开辟新线程,其程序运行路径还是仅仅有一条,这样就没有达到写线程的目的。

測试代码例如以下

代码例如以下:

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.run();
t2.run();
}
}

输出结果为

>>当前进程为:main

>>当前进程为:main

改为用start方法:

代码例如以下:

package thread;
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 类型),看是否会出现脏读等现象:

代码例如以下:

public class Test implements Runnable {
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 class Common implements Runnable {
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 class Common implements Runnable {
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 class Common implements Runnable {
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 class Common implements Runnable {
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多线程具体总结相关推荐

  1. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  2. Java多线程读取本地照片为二进制流,并根据系统核数动态确定线程数

    Java多线程读取图片内容并返回 1. ExecutorService线程池 2. 效率截图 3. 源码 1. ExecutorService线程池 ExecutorService线程池,并可根据系统 ...

  3. Java多线程,Thread,Runnable,Callable Task,Future<Task>,CompletionService

    一.Java多线程的方法 1. 继承 Thread 2. 实现 Runnable 3. 实现 Callable 可以有返回值 package com.test;import java.util.Arr ...

  4. 【收藏】Java多线程/并发编程大合集

    (一).[Java并发编程]并发编程大合集-兰亭风雨    [Java并发编程]实现多线程的两种方法    [Java并发编程]线程的中断    [Java并发编程]正确挂起.恢复.终止线程    [ ...

  5. 40个Java多线程问题总结

    (转) 这篇文章作者写的真是不错 40个问题汇总 1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓"知其然知其所 ...

  6. Java多线程编程实战:模拟大量数据同步

    背景 最近对于 Java 多线程做了一段时间的学习,笔者一直认为,学习东西就是要应用到实际的业务需求中的.否则要么无法深入理解,要么硬生生地套用技术只是达到炫技的效果. 不过笔者仍旧认为自己对于多线程 ...

  7. Java多线程学习处理高并发问题

    在程序的应用程序中,用户或请求的数量达到一定数量,并且无法避免并发请求.由于对接口的每次调用都必须在返回时终止,因此,如果接口的业务相对复杂,则可能会有多个用户.调用接口时,该用户将冻结. 以下内容将 ...

  8. Java多线程常见面试题及答案汇总1000道(春招+秋招+社招)

    Java多线程面试题以及答案整理[最新版]Java多线程高级面试题大全(2021版),发现网上很多Java多线程面试题都没有答案,所以花了很长时间搜集,本套Java多线程面试题大全,汇总了大量经典的J ...

  9. java多线程编程01---------基本概念

    一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...

  10. Java多线程的同步机制(synchronized)

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...

最新文章

  1. Monkey基本使用(转载)
  2. 对于一个IE8兼容性问题的反思
  3. 中海达手部链接电脑安装软件_山东水文局:较大含沙量条件下中海达ADCP外接测深仪测流系统试验成功...
  4. 力扣——删除重复的电子邮箱(数据库的题
  5. 售价19000元!华为发布全新5G折叠屏手机Mate Xs
  6. SAP License:心情
  7. 深入理解C#的装箱和拆箱
  8. 【原】c#对xml的操作
  9. VB程序打包再安装之后不含源码
  10. Linux下载HTTP文件
  11. 外贸客户催货催的很急的原因
  12. 【keepass】每次打开软件总是弹出指定的文件不存在,如何关闭和清除以前打开数据库的文件记录?
  13. 如何编辑PDF文件?简单好用的编辑方法分享
  14. Java-Java绘图坐标体系
  15. 陈东方 c语言程序设计基础实验与题解(答案),C语言程序设计基础实验与题解 黄远林 陈东方 李顺新...
  16. tlac100怎么添加ap_TP-link AC300无线控制器和AP怎么设置
  17. leetcode765.情侣牵手
  18. “东数西算”下数据中心的液冷GPU服务器如何发展?
  19. 【Paddle笔记】体验PaddleBoBo虚拟主播自动播报快速生成项目
  20. 编辑们的必备软件之小牛文件怎么恢复数据

热门文章

  1. 分享50佳高质量免费按钮图标资源(上篇)[zz]
  2. 预告:公共语言运行库(CLR)开发系列课程(4):COM Interop进阶
  3. SQLServer查询所有表所有字段包含xx的信息
  4. 【转贴】学习Linux的几点忠告
  5. AndroidStudio_安卓原生开发_自定义服务器Token验证_MD5加密方法---Android原生开发工作笔记156
  6. 持续集成部署Jenkins工作笔记0011---配置构建命令并手动执行一次构建
  7. DataBseDesign工作笔记004---PowerDesigner导入sql脚本生成物理模型
  8. SpringColoud学习笔记007---杂七杂八001--@Resource与@Autowired注解的区别
  9. 微信小程序开发学习笔记005--微信小程序组件详解
  10. Android异常总结---res\layout\addUser.xml: Invalid file name: must contain only [a-z