Java中的线程--线程的互斥与同步通信
Java中的线程之前也提到过,但是还是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下。
一、线程中的互斥
线程安全中的问题解释:线程安全问题可以用银行中的转账
例题描述:
线程A与线程B分别访问同一个对象的方法,这样就会存在线程安全的问题,方法的作用是打印出字符串中的每一个字符,方法如下:
public void output(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
线程A和线程B代码如下:(直接写在了init()方法中了)
private void init() {
outputer outputer = new outputer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("songshengchao");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("songxiaochao");
}
}
}).start();
}
测试一下,肯定会出现线程不安全的问题,这是母庸质疑的事实,测试代码如下:
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
三种解决办法,代码如下:
public class outputer {
public void output(String name) {
int len = name.length();
synchronized (this) { // 传进来当前调用方法的对象,要求线程用的是同一个对象
//synchronized (outputer.class) { // 这样和outputer3方法达到线程互斥
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
// 方法上的锁对象用的就是this当前对象
public synchronized void output2(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
// output3 想和output方法达到线程互斥
public static synchronized void output3(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
注意:至于第三种静态的synchronized方法,在和第一个方法共用时,第一种方法一定是用当前对象的class字节码文件,才能确保两个方法用的同一个对象。
二、线程互斥与通信的经典面试题
面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在主线程循环100次,如此循环50次,程序如何写???
经验之谈,设计思想,设计思路:
要用到共同数据(包括同步锁)的若干个方法应该归在同一个类上,这种设计正好提现了程序的高内聚与健壮性
思路:先写主线程与子线程的循环,然后在考虑轮流执行。先考虑循环,代码如下:
public class TraditionalThreadCommunication {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
synchronized (TraditionalThreadCommunication.class) {
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequece of" + j + ", loop of " + i);
}
}
}
}
}).start();
// 本身main方法就是主线程,直接可以写循环代码
for (int i = 1; i <= 50; i++) {
synchronized (TraditionalThreadCommunication.class) {
for (int j = 1; j <= 100; j++) {
System.out.println("main thread sequece of" + j + ", loop of " + i);
}
}
}
}
}
代码优化,用面向对象的思想,将那些代码放到一个公共的类中,然后执行类中的不同方法,优化成一个公共的类,代码如下:
public class Business {
public synchronized void sub(int i){
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequece of" + j + ", loop of " + i);
}
}
public synchronized void main(int i){
for (int j = 1; j <= 100; j++) {
System.out.println("main thread sequece of" + j + ", loop of " + i);
}
}
}
----------------------------------------------------------------------------------------------
public class TraditionalThreadCommunication {
public static void main(String[] args) {
Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start();
// 本身main方法就是主线程,直接可以写循环代码
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
}
最终的完整代码如下(详细注释):
public class Business {
// 是否是子线程执行 默认子线程先执行
private boolean bShouldSub = true;
public synchronized void sub(int i) {
// 不是子线程应该执行 让给主线程 子线程执行等待的方法
while (!bShouldSub) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequece of" + j + ", loop of " + i);
}
// 子线程执行完毕后 让给主线程执行
bShouldSub = false;
// 唤醒主线程
this.notify();
}
public synchronized void main(int i) {
// 是子线程应该执行 让给子线程执行 主线程执行等待的方法
while (bShouldSub) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("main thread sequece of" + j + ", loop of " + i);
}
// 主线程执行费完毕后 交给子线程执行
bShouldSub = true;
// 唤醒子线程
this.notify();
}
}
------------------------------------------------------------------------------------------------
public class TraditionalThreadCommunication {
public static void main(String[] args) {
Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start();
// 本身main方法就是主线程,直接可以写循环代码
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
}
Java中的线程--线程的互斥与同步通信的更多相关文章
- Java中的守护线程 & 非守护线程(简介)
Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...
- Java中如何创建线程
Java中如何创建线程 两种方式:1)继承Thread类:2)实现Runnable接口. 1.继承Thread类 继承Thread类,重写run方法,在run方法中定义需要执行的任务. class M ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- Java中怎样创建线程安全的方法
面试问题: 下面的方法是否线程安全?怎样让它成为线程安全的方法? class MyCounter { private static int counter = 0; public static int ...
- ThreadLocal,Java中特殊的线程绑定机制
在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...
- Java中的守护线程和非守护线程(转载)
<什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...
- Java中的守护线程
守护线程的概念 在java中有两种线程,守护线程和非守护线程,其两者并没有本质的区别,唯一的区别就是当前的用户线程退出的时候,若只存在唯一的A线程,若A线程为守护线程,那么JVM将会直接退出,否则JV ...
- (转)Java中的守护线程
Java的守护线程与非守护线程 守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户线程和守护线程 所谓守护 ...
- Java中的守护线程——daemon
絮叨 Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 定义:守护线程(aka:服务线程),在没有用户线程可服务时会自动离开. 优先级:守护线程的优先级 ...
- Java-ThreadLocal,Java中特殊的线程绑定机制
在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...
随机推荐
- IT兄弟连 JavaWeb教程 JSP动作指令
JSP动作指令语法 JSP动作指令语法格式如下: <jsp:动作名 属性名1="属性值1" 属性名2="属性值2" ... %> JSP具体动作指令 ...
- 字符串最小表示初探 By cellur925
我们考虑有一个字符串,可以从这个字符串的不同位置出发,把这个字符串大声朗读出来,当到字符串末端的时候再从头开始读,直到回到"梦开始的地方". 设字符串长度为\(n\),那么有\(n ...
- JQuery Easyui/TopJUI表格基本的删除功能(删除当前行和多选删除)
需求:数据表格datagrid实现删除当前行和多选删除的功能. html <a href="javascript:void(0)" data-toggle="top ...
- 「开源」SpringCloud+vue搭建的商城项目
最近在研究SpringCloud,看到一个基于SpringCloud+vue搭建的模拟商城项目.用来辅助学习SpringCloud企业级开发还是很有帮助的.强烈推荐!! 源码地址在最后. spring ...
- 关于JS中的call()方法和apply() 暂时只接触到call() 等接触到apply()再回头来看
1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法. 2. 相同点:这两个方法的作用是一样的. 都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖 ...
- Vue文件 引入.js文件 的组件
Vue.component('remote-script', { render: function (createElement) { var self = this; return createEl ...
- go系列(5)- beego自己写controller
前边的系列文章已经讲述了如何安装环境, beego的处理逻辑都是在Controller里面完成的,下面就写一个最简单的Controller. 我们在写自己的controller的时候,一定要继承bee ...
- JS的this原理
转载阮一峰博客: www.ruanyifeng.com/blog/2018/06/javascript-this.html 一.问题的由来 学懂 JavaScript 语言,一个标志就是理解下面两 ...
- JSDOM获取子节点的一些方法
一般情况获取子节点,通过找到查找父节点的ID或者class类名,来获取父节点,再通过children属性,得到子节点的数组: 之前在另外一篇随笔中说过,如果使用另一个属性childNode,会把注释. ...
- MFC 创建UI线程
对于windows来说,所有的线程都是一样的,但MFC却把线程区分为两种:用户界面(UI)线程和工作者线程.用户界面线程具有消息循环而工作者线程没有.UI线程可以创建窗口并给这些窗口发送消息,工作者线 ...