《Java多线程编程核心技术》读后感(二)
方法内的变量为线程安全
package Second;
public class HasSelfPrivateNum {
public void addI(String username) {
try {
int num = 0;
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package Second;
public class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
package Second;
public class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
package Second;
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef);
athread.start();
ThreadB bthread = new ThreadB(numRef);
bthread.start();
}
}

实例变量非线程安全
如果对象中有多个实例变量,则运行结果有可能出现交叉的情况
如果对象仅有一个变量,则有可能出现覆盖的情况
package Second;
public class HasSelfPrivateNum {
private int num = 0;
public void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

解决方案:
package Second;
public class HasSelfPrivateNum {
private int num = 0;
synchronized public void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

实验结论:在两个线程访问同一个对象重点额同步方法时一定是线程安全的
多个对象多个锁
其他代码实现代码如上
package Second;
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef1);
athread.start();
ThreadB bthread = new ThreadB(numRef2);
bthread.start();
}
}

上面例子是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却以异步的方式运行的。本实例由于创建了两个业务对象,在系统中产生出两个锁,所以运行结果是异步的

sychronized方法与锁对象
证明前面讲述的线程锁的是对象
package Second;
public class MyObject {
public void methodA() {
try {
System.out.println("begin methodA threadName="
+ Thread.currentThread().getName());
Thread.sleep(500000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package Second;
public class ThreadA extends Thread {
private MyObject object;
public ThreadA(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
package Second;
public class ThreadB extends Thread {
private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
package Second;
public class Run {
public static void main(String[] args) {
MyObject object = new MyObject();
ThreadA a = new ThreadA(object);
a.setName("A");
ThreadB b = new ThreadB(object);
b.setName("B");
a.start();
b.start();
}
}

package Second;
public class MyObject {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

调用关键字synchronized声明的方法一定是在排队运行中,另外需要牢牢记住“共享”两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,就没有同步的必要
package chapter02.section01.thread_2_1_4.project_2_synchronizedMethodLockObject2;
public class MyObject {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
try {
System.out.println("begin methodB threadName="
+ Thread.currentThread().getName() + " begin time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package chapter02.section01.thread_2_1_4.project_2_synchronizedMethodLockObject2;
public class ThreadA extends Thread {
private MyObject object;
public ThreadA(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
package chapter02.section01.thread_2_1_4.project_2_synchronizedMethodLockObject2;
public class ThreadB extends Thread {
private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodB();
}
}
package chapter02.section01.thread_2_1_4.project_2_synchronizedMethodLockObject2;
public class Run {
public static void main(String[] args) {
MyObject object = new MyObject();
ThreadA a = new ThreadA(object);
a.setName("A");
ThreadB b = new ThreadB(object);
b.setName("B");
a.start();
b.start();
}
}

虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronized类型的方法
package chapter02.section01.thread_2_1_4.project_2_synchronizedMethodLockObject2;
public class MyObject {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void methodB() {
try {
System.out.println("begin methodB threadName="
+ Thread.currentThread().getName() + " begin time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


脏读
发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了
package Second;
public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username="
+ username + " password=" + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue() {
System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username
+ " password=" + password);
}
}
package Second;
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B", "BB");
}
}
package Second;
public class Test {
public static void main(String[] args) {
try {
PublicVar publicVarRef = new PublicVar();
ThreadA thread = new ThreadA(publicVarRef);
thread.start();
Thread.sleep(200);
publicVarRef.getValue();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

出现脏读是因为getValue()并不是同步的,所以可以在任意时候进行调用
package Second;
public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username="
+ username + " password=" + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void getValue() {
System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username
+ " password=" + password);
}
}


synchronized锁重入
也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这也证明在一个synchronized方法/块内的内部调用本类的其他synchronized方法/块时,是用户可以得到锁的。
package Second;
public class Service {
synchronized public void service1() {
System.out.println("service1");
service2();
}
synchronized public void service2() {
System.out.println("service2");
service3();
}
synchronized public void service3() {
System.out.println("service3");
}
}
package Second;
public class MyThread extends Thread {
@Override
public void run() {
Service service = new Service();
service.service1();
}
}
package Second;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}


package Second;
public class Main {
public int i = 10;
synchronized public void operateIMainMethod() {
try {
i--;
System.out.println("main print i=" + i);
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package Second;
public class Sub extends Main {
synchronized public void operateISubMethod() {
try {
while (i > 0) {
i--;
System.out.println("sub print i=" + i);
Thread.sleep(100);
this.operateIMainMethod();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package Second;
public class MyThread extends Thread {
@Override
public void run() {
Sub sub = new Sub();
sub.operateISubMethod();
}
}
package Second;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
Main main = new Main();
System.out.println(main.i);
}
}

此实验表明,当存在父子类继承关系时,子类是完全可以通过“”可重入锁“”调用父类的同步方法的
出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放
package Second;
public class Service {
synchronized public void testMethod() {
if (Thread.currentThread().getName().equals("a")) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ " run beginTime=" + System.currentTimeMillis());
int i = 1;
while (i == 1) {
if (("" + Math.random()).substring(0, 8).equals("0.123456")) {
System.out.println("ThreadName="
+ Thread.currentThread().getName()
+ " run exceptionTime="
+ System.currentTimeMillis());
Integer.parseInt("a");
}
}
} else {
System.out.println("Thread B run Time="
+ System.currentTimeMillis());
}
}
}
package Second;
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
package Second ;
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
package Second;
public class Test {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("a");
a.start();
Thread.sleep(500);
ThreadB b = new ThreadB(service);
b.setName("b");
b.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

线程a出现异常并释放锁,线程b进入方法正常打印
同步不具有继承性
package Second;
public class Main {
synchronized public void serviceMethod() {
try {
System.out.println("int main 下一步 sleep begin threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int main 下一步 sleep end threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package Second;
public class Sub extends Main {
@Override
synchronized public void serviceMethod() {
try {
System.out.println("int sub 下一步 sleep begin threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int sub 下一步 sleep end threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
super.serviceMethod();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package Second;
public class MyThreadA extends Thread {
private Sub sub;
public MyThreadA(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}
package Second;
public class MyThreadB extends Thread {
private Sub sub;
public MyThreadB(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}

所以还要在子类的方法中添加synchronized关键字

《Java多线程编程核心技术》读后感(二)的更多相关文章
- Springboot揭秘-快速构建微服务体系-王福强-2016年5月第一次印刷
JavaConfig项目: spring IOC有一个非常核心的概念——Bean.由Spring容器来负责对Bean的实例化,装配和管理.XML是用来描述Bean最为流行的配置方式.Spring可以从 ...
- 《SpringBoot揭秘 快速构建微服务体系》读后感(一)
SpringIOC IOC有两种方式:一种是DI,另一种是DL,即Dependency Lookup(依赖查找).前者是当前软件实体被动接受其依赖的其他组件被IoC容器注入,而后者则是当前软件实体主动 ...
- 《SpringBoot揭秘 快速构建微服务体系》读后感(五)
应用日志和spring-boot-starter-logging 快速web应用开发与spring-boot-starter-web 1.项目结构层面的约定
- 《SpringBoot揭秘 快速构建微服务体系》读后感(三)
SpringApplication:SpringBoot程序启动的一站式解决方案 深入探索SpringApplication执行流程 因为书上的版本是1.2的,比较老,这里参考http://blog. ...
- 《SpringBoot揭秘 快速构建微服务体系》读后感(二)
最简单的springBoot应用 package com.louis.test; import org.springframework.boot.SpringApplication; import o ...
- 《SpringBoot揭秘 快速构建微服务体系》读后感(四)
再谈自动配置 基于条件的自动配置 调整自动配置的顺序
- [高清] SpringBoot揭秘快速构建微服务体系
------ 郑重声明 --------- 资源来自网络,纯粹共享交流, 如果喜欢,请您务必支持正版!! --------------------------------------------- 下 ...
- SpringBoot 快速构建微服务体系 知识点总结
可以通过http://start.spring.io/构建一个SpringBoot的脚手架项目 一.微服务 1.SpringBoot是一个可使用Java构建微服务的微框架. 2.微服务就是要倡导大家尽 ...
- SpringBoot揭秘:快速构建微服务体系
chapter 2: 饮水思源:回顾与探索Spring框架本质 IoC其实有两种方式,一种是DI(dependency Injection),一种是DL(dependency Lookup 依赖查找, ...
- 通过GeneXus如何快速构建微服务架构
概览 “微服务”是一个非常广泛的话题,在过去几年里,市面上存在着各种不同的定义. 虽然对这种架构方式没有一个非常精确的定义,但仍然有一些概念具有代表性. 微服务有着许多围绕业务能力.自动化部署.终端智 ...
随机推荐
- python 基础 6.2 raise 关键字使用
一. raise 关键字 raise 用来触发异常 语法如下: raise[Exception [,args [,traceback]]] 语句中Exception 是异常 ...
- 网卡配置bond
在实际的生产环境中,服务器都需要配置bond环境的,以提高安全性及均衡能力.我公司网卡配置的是mode=1 类型,mode=1 是主备模式,当其中一块网卡不能工作时,另一块网卡立即代替.以下是mode ...
- EasyPlayerPro windows播放器之多窗口播放音量控制方法
EasyPlayerPro-win基础版本的音频播放为单一通道播放,即同一时间仅允许一个通道播放声音,现应客户需求,在基础版本上实现独立的音频播放,即每个通道可同时播放视频和音频; 设计思路 将音频播 ...
- EasyNVR H5无插件摄像机直播解决方案前端解析之:如何在播放界面添加实时云台控制界面
如何在播放器上加一个云台控制界面 问题: 对于实时直播的视频播放, 由于播放页面客观样式要求(一个播放器占据了整个页面),因此很难找出很合理的空间来放置其他功能按钮的位置(比如配合实时是平的云台控制界 ...
- sql中decode()重要函数使用
decode()函数简介: 主要作用:将查询结果翻译成其他值(即以其他形式表现出来,以下举例说明): 使用方法: Select decode(columnname,值1,翻译值1,值2,翻译值2,.. ...
- UITableView的headerView和headerInsectionView
UITableView有两个headerView:tableHeaderView.和headerInsectionView(组头视图). 给tableView添加这两个View:tableHead ...
- Android进程的生命周期
Android系统想要永久的保留一个应用进程差点儿是不可能的.所以系统就须要不断的释放老的或者不太重要的进程以便腾出足够的内存空间来执行新的或者更重要的进程,那么系统怎样决定哪个进程应该保留哪个应该杀 ...
- 【linux】crontab的环境变量问题
今天遇到一个奇怪的问题,同样一个脚本,手动执行没问题,加入到crontab中,就出现无法运行的情况,第一反应是环境变量问题 环境说明: 操作系统:centos 用户:test用户通过sudo su切换 ...
- ls bash: cannot create temp file for here-document: No space left on device
出现这种问题,一般是磁盘空间满了,或者是inode满了 使用命令: df -h查询磁盘空间 df -i 查询inode占用 Filesystem Inodes IUsed IFree IUse% Mo ...
- java调用shell命令及脚本
shell脚本在处理文本及管理操作系统时强大且简单,将shell脚本结合到应用程序中则是一种快速实现的不错途径本文介绍使用java代码调用并执行shell 我在 -/bin/ 目录下写了jbossLo ...