java线程(2)-线程同步

本节主要是在前面吃苹果的基础上发现问题,然后提出三种解决方式

1.线程不安全问题

什么叫线程不安全呢

即当多线程并发访问同一个资源对象的时候,可能出现不安全的问题

对于前一章例子中,使用接口实现方式时会有重复现象,使用接口方式时我们还没有发现明显的现象,但是这并不代表原来的代码没有问题

我们发现没有问题,必须要有这个意识:看不到问题,有可能是我们经验太少,或者问题出现的不够明显。

如果问题不够明显,我们可以使用Thread.sleep()方法,正在运行的线程暂停,此时会执行另外的进程,如此可以更方便的看到问题

注意:

在线程的run方法上不能使用throws来声明抛出异常,只能在方法中使用try-catch来处理异常

原因是:子类覆盖父类的原则,子类不能抛出新的异常。

在Runnable接口中的run方法,都没有抛出异常,所以子类中也不能对这个方法抛出异常

父类中:public abstract void run();

解决方法的原理

回到我们用继承方法的问题上来,如何才能让一个线程没有执行完时另一个线程不会操作一些动作呢。

解决方案是:保证某一些的动作的同步完成。即保证打印苹果和苹果总数-1操作,必须同步,作为一个单元来执行

具体的解决方法有3个:

方式1:同步代码块

方式2:同步方法

方式3:锁机制(lock)

下面分别实现三种方法

2.线程同步

思想是把不能中断或者分开执行的代码绑定在一起执行,使其不能在执行中中断

2.1.同步代码块

语法:

synchronized(同步锁)
{
需要同步操作的代码
}

同步锁:

为了保证没一个线程都能正常执行原子操作,java引入了线程同步机制

同步锁又被称为:同步监听对象/同步监听器/互斥锁

其实就是相当与一把只有一个坑位的卫生间门上的锁,当里面有人的时候,门必须要被锁上,等人出来了再把锁打开

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁

java程序运行使用任何对象作为同步锁,但是一般的,我们是用当前并发访问的共同资源作为同步监听对象

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程,只能在外面等着

示例代码:

//接口实现方式
class Apple1 implements Runnable{
private int num = 50; public void run(){
for (int i = 0; i < 50; i++) {
synchronized (this){
if (num > 0){
System.out.println(Thread.currentThread().getName()+"吃了编号为"+ num +"的苹果");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
}
} }
}
} public class appleImplements {
public static void main(String[] args) {
//创建桑个线程
Apple1 a = new Apple1();
new Thread(a,"小A").start();
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}

注意:因为继承方法没有不能实现资源的共享,所以这里的例子都是以接口方式实现的。

2.2.同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法。保证A线程执行该方法的时候,其他线程只能在外面等着

语法:

synchronized public void doWork(){
//TODO 要执行的动作
}

同步锁是谁:

对于非static方法,同步锁就是this

对于static方法,我们使用当前方法所在的类的字节码对象(Apple2.class)

注意:不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有的功能,好比是多个线程出现串行

代码实现:


//接口实现方式
class Apple2 implements Runnable{
private int num = 500; public void run(){
for (int i = 0; i < 500; i++) {
eat(); }
} synchronized private void eat(){
if (num > 0){
System.out.println(Thread.currentThread().getName()+"吃了编号为"+ num +"的苹果");
try {
Thread.sleep(10);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
}
}
} public class appleImplements {
public static void main(String[] args) {
//创建桑个线程
Apple2 a = new Apple2();
new Thread(a,"小A").start();
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}

2.3.同步锁(Lock)

Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象

语法:



思想就是,在执行要绑定的代码之前,上一把锁,执行完之后呢,就把锁打开,这样别的线程就能继续了。

在使用这个锁之前呢,要先new一个锁出来

代码示例;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; //接口实现方式
class Apple4 implements Runnable{
private int num = 50;
private final Lock lock = new ReentrantLock();//创建一个锁对象
public void run(){
for (int i = 0; i < 50; i++) {
eat(); }
} private void eat(){
//进入方法:立马加锁
lock.lock();//获取锁
if (num > 0){
try {
System.out.println(Thread.currentThread().getName()+"吃了编号为"+ num +"的苹果");
Thread.sleep(100);//模拟网络延迟
num--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//执行完,走人,并把锁打开
lock.unlock();//释放锁
}
}
}
} public class appleImplements {
public static void main(String[] args) {
//创建桑个线程
Apple4 a = new Apple4();
new Thread(a,"小A").start();
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}

2.4.synchronized的好与坏

好处:保证了多线程并并发访问时的同步操作,避免线程的安全性问题

缺点:使用synchronized方法/代码块的性能比不用要低一些

建议:尽量减小synchronized的作用域

第22章 java线程(2)-线程同步的更多相关文章

  1. 《Java并发编程的艺术》 第9章 Java中的线程池

    第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...

  2. 第9章 Java中的线程池 第10章 Exector框架

    与新建线程池相比线程池的优点 线程池的分类 ThreadPoolExector参数.执行过程.存储方式 阻塞队列 拒绝策略 10.1 Exector框架简介 10.1.1 Executor框架的两级调 ...

  3. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  4. java 22 - 12 多线程之解决线程安全问题的实现方式1

    从上一章知道了多线程存在着线程安全问题,那么,如何解决线程安全问题呢? 导致出现问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 上一章的程序,上面那3条都具备, ...

  5. “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第十五章:当后台线程遇到finally

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. 《深入理解Java虚拟机》-----第12章 Java内存模型与线程

    概述 多任务处理在现代计算机操作系统中几乎已是一项必备的功能了.在许多情况下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与它的存储和通信子系统速 ...

  8. Java多线程面试题:线程锁+线程池+线程同步等

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

  9. Java多线程 3 线程同步

    在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...

随机推荐

  1. Object.prototype和Function.prototype一些常用方法

    Object.prototype 方法: hasOwnProperty 概念:用来判断一个对象中的某一个属性是否是自己提供的(主要是判断属性是原型继承还是自己提供的) 语法:对象.hasOwnProp ...

  2. iOS 9 强制横屏

    首先在plist 文件中 Supported interface orientations 选项 只留下一个 portrait 屏幕强制横屏 使用以下代码 self.navigationControl ...

  3. UI笔记

    tableView 自定义cell 还有之前的轮播图整理

  4. 关于Fragment你所需知道的一切!

    转载自刘明渊 的博客地址:http://blog.csdn.net/vanpersie_9987 Fragment 是 Android API 中的一个类,它代表Activity中的一部分界面:您可以 ...

  5. React Native知识6-NavigatorIOS组件

    NavigatorIOS包装了UIKit的导航功能,可以使用左划功能来返回到上一界面.本组件并非由Facebook官方开发组维护.这一组件的开发完全由社区主导.如果纯js的方案能够满足你的需求的话,那 ...

  6. 4-printf & scanf函数

    一.printf函数 这是(printf和scanf)在stdio.h中声明的一个函数,因此使用前必须加入#include <stdio.h> 1.用法 1> printf(字符串) ...

  7. JAVA实现图片裁剪

    /** * 裁剪图片 * @param src 源图片 * @param dest 裁剪后的图片 * @param x 裁剪范围的X坐标 * @param y 裁剪范围的Y坐标 * @param w ...

  8. php 日期计算 总结

    1 加 2天 date("Y-m-d", strtotime("$today + 2 days")); date("Y-m-d", strt ...

  9. Android Fragment生命周期

    Fragment与Activity的生命周期关系: 刚打开Activity:Fragment onAttach > Fragment onCreate > Fragment onCreat ...

  10. 干货发布:VSS文件清理工具

    一.功能:1.删除VSS文件(以.vssscc,.scc,.vspscc为后缀的文件)2.去除文件解决方案文件和C#项目文件中的VSS标签3.删除Bin和Obj目录 二.开发工具:vs 2010 三. ...