Java学习系列(十五)Java面向对象之细谈线程、线程通信(下)
竞争资源(共享资源):如果有多条线程需要并发访问、并修改某个对象,该对象就是“竞争资源”。为了避免多个线程"自由竞争”修改共享资源所导致的不安全问题。
线程同步(像Vector、Hashtable等都是线程安全的):
解决线程异步有两种方式:
1)同步代码块(需要显式的指定同步监视锁);
2).同步方法(相当于使用方法的调用者,如果方法是实例方法,相当于this为同步监视锁;如果方法是类方法,相当于类作为同步监视锁)。
它们的实现机制是完全相同的,当线程要进入被"同步监视锁"监视的代码之前,线程必须先获得“同步监视锁”,这样就可以保证在任意一个时刻,只有一条线程能进入被"同步监视锁"监视的代码。从程序逻辑来看,选择“竞争资源”作为同步监视锁。
举例说明1--同步代码块(由于代码都是些属性,所以这里只抽取了片段,掌握思想即可):
// 同步代码块,synchronized后的括号中的对象被称为同步锁synchronized (account) {if (account.getBalance() > drawAmount) {System.out.println("取钱成功,吐出钱数:" + drawAmount);account.setBalance(account.getBalance() - drawAmount);System.out.println("还剩余额为:" + account.getBalance());} else {System.out.println("取钱失败,余额不足!");}}
举例说明2:--同步方法,以this作为同步监视锁。
public synchronized void draw(double drawAmount){if (getBalance() > drawAmount) {System.out.println("取钱成功,吐出钱数:" + drawAmount);setBalance(getBalance() - drawAmount);System.out.println("还剩余额为:" + getBalance());} else {System.out.println("取钱失败,余额不足!");}}
线程同步的关键:任何同步监视器监视的代码之前,必须先对同步监视器加锁。
何时释放对【同步监视器】的加锁?
1.同步代码块或同步方法执行完成。
2.在代码中遇到了break、return语句跳出该代码块。
3.执行同步代码块或同步方法时遇到未捕获的异常时。
4.调用了同步监视器的wait()方法。
【注意】使用sleep()、yield()都不会释放。
线程通信:
1.如果不加控制,多个线程“自由”地并发执行。
2.可以通过同步,来解决多个线程并发访问竞争资源的问题。线程安全,必然降低性能。
3.如果希望线程之间能更有序地执行。
线程组ThreadGroup与未处理的异常:
怎样把线程放入指定的线程组中呢? --在创建一个Thread实例时,通过传入的ThreadGroup对象,即可将该线程放入指定的线程组中,进而通过线程组对这批线程进行整体的管理。
ThreadGroup提供了如下两个方法:setDaemon(boolean daemon) 控制将线程组本身都设置为后台线程组,并不是将它所包含的线程设为后台线程。setMaxPriority(优先级):它是设置该线程组已有的线程的优先级不会受影响,对以后新添加的线程的优先级才会有影响。
对线程异常进行处理:在JDK1.5之前,系统会自动回调它所在线程组的uncaughtException(Thread t,Throwable e)方法来修复该异常;在JDK1.5之后,线程允许自行设置“异常处理器”,无需线程组。public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为所有线程设置默认的异常处理器。public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为当前线程实例设置异常处理器。
举列说明1(jdk1.5之前):
public class ThreadExceptionTest implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i/ (i - 20));}}public static void main(String[] args) {// 创建一个线程组ThreadGroup tg = new ThreadGroup("mytg") {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName() + "出现了异常,信息是:" + e.getMessage());}};ThreadExceptionTest test = new ThreadExceptionTest();new Thread(tg, test).start();}}
举列说明2(jdk1.5之后):
public class ThreadExceptionTest implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i/ (i - 20));}}public static void main(String[] args) {// 为所有线程设置默认的异常处理器Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName() + "出现了异常,信息是:" + e.getMessage());}});ThreadExceptionTest test = new ThreadExceptionTest();new Thread(test).start();}
}
线程池(Pool):池的本质,就是一种“缓存”技术。是否要缓存一个对象,要看该对象的创建成本。
Executors --创建线程池,线程工厂的工具类。ExecutorService:它就是线程池。
缓存的本质:牺牲空间(内存)来换取时间。线程对象的创建成本比较大(尽管比创建进程的成本小的多,但相对普通java对象,Thread的创建成本依然较大),为解决这个问题,我们用线程池。
编程步骤:
1.通过Executors的静态工厂方法创建ExecutorService或ScheduledExecutorService
2.调用ExecutorService的方法提交线程即可。
3.调用线程池的.shutdown方法关闭线程池。
举例说明1:
public class ThreadPoolTest implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ",i=" + i);}}public static void main(String[] args) {ExecutorService es = Executors.newFixedThreadPool(10);es.submit(new ThreadPoolTest());es.shutdownNow();// 关闭线程池}
}
举例说明2:
public class ThreadPoolTest implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ",i=" + i);}}public static void main(String[] args) {ScheduledExecutorService es = Executors.newScheduledThreadPool(10);// 延迟5s,以后每隔2s执行一次run方法es.scheduleAtFixedRate(new ThreadPoolTest(), 5, 2, TimeUnit.SECONDS);}
}
Java学习系列(十五)Java面向对象之细谈线程、线程通信(下)相关推荐
- Java学习系列(十八)Java面向对象之基于UDP协议的网络通信
UDP协议:无需建立虚拟链路,协议是不可靠的. A节点以DatagramSocket发送数据包,数据报携带数据,数据报上还有目的目地地址,大部分情况下,数据报可以抵达:但有些情况下,数据报可能会丢失 ...
- Java学习系列(十六)Java面向对象之基于TCP协议的网络通信
TCP/IP的网络分层模型:应用层(HTTP/FTP/SMTP/POPS...),传输层(TCP协议),网络层(IP协议,负责为网络上节点分配唯一标识),物理层+数据链路层). IP地址用于标识网络中 ...
- Java学习系列(十九)Java面向对象之数据库编程
JDBC(Java Data Base Connectivity:java数据库连接):它定义了一组标准的操作数据库的接口,既然是接口,那它就是一种规范,是Java操作数据库的技术规范. Java数据 ...
- Java学习系列(十二)Java面向对象之序列化机制及版本
序列化:内存中的Java对象<-->二进制流 目的:a)有时候需要把对象存储到外部存储器中持久化保存,b)还有时候,需要把对象通过网络传输. 可序列化的对象,Java要求可序列化的类实现下 ...
- Java学习系列(十四)Java面向对象之细谈线程、线程通信(上)
线程与进程的关系: 进程 --运行中的程序.进程有如下特征: 1).独立性.拥有自己的资源,拥有自己独立的内存区. 通常来说,一个进程的内存空间,是不允许其他进程访问的. 但像Windows,如A进程 ...
- Java学习系列(十)Java面向对象之I/O流(上)
IO流 我们知道应用程序运行时数据是保存在内存中的,但由于内存中的数据不可持久保存(如断电或程序退出时数据会丢失),因此需要一种手段将数据写入硬盘或读入内存.面向IO流编程就是一种很好的选择.IO:I ...
- java学习记录十五:集合二Collections、Set、Map
java学习记录十五:集合二 一.Collections工具类 一.解释 二.常用方法 1.打乱集合顺序 2.按照默认规则排序 3.按指定规则排序 4.批量添加元素 二.可变参数 一.解释 二.写法 ...
- Java学习 第十五天
Java学习 第十五天 第一章 StringBuilder类 1.1 字符串的不可变 1.2 StringBuilder概述 1.3 构造方法 1.4 两个常用方法 1.4.1 append方法 1. ...
- WorldWind学习系列十五:如何切割影像和DEM数据及其在WW中的应用配置
原文转自:http://www.cnblogs.com/wuhenke/archive/2010/01/03/1638499.html WorldWind学习系列十四中我从代码上分析如何加载DEM数据 ...
最新文章
- TypeError: ‘instancemethod‘ object has no attribute ‘__getitem__‘
- Windows LTSC、LTSB、Server 安装 Windows Store 应用商店
- linux下查看网卡型号
- gitlab远程提交
- 【学术相关】研究生哪些行为可以在导师那超加分?你做到了吗?
- 工作中男女程序员对比,没注意原来差距这么大!你中招了吗?
- iOS pickerView(所有类型一网打尽)
- mysql操作符_MySql 中的=操作符
- Pytorch——DataLoader(批训练)
- Android ListView + ArrayAdapter、SimpleAdapter、BaseAdapter实现列表
- Swift - 实现tableView单选系统样式
- 穿越迷宫的函数c语言,数据结构课外实践题库(26页)-原创力文档
- linux服务器测试接口命令,Linux 下 TCP/UDP 端口测试及验证方法说明
- screentogif能录制声音吗_一款免费且强大的gif动画录制工具,再也不愁录动画!...
- netkeeper客户端_中国电信创翼客户端下载
- 2022-华为-大数据研发工程师-秋招面经
- 使用KMS批量激活操作系统
- 每日一句_《南柯子·池水凝新碧》
- 【实战案例】python进行自动网上考试
- Swoole---Http(协程风格)