java丐帮_java多线程学习笔记(三)
java多线程下的对象及变量的并发访问
上一节讲到,并发访问的时候,因为是多线程,变量如果不加锁的话,会出现“脏读”的现象,这个时候需要“临界区”的出现去解决多线程的安全的并发访问。(这个“脏读”的现象不会出现在方法内部的私有变量中,因为其私有的特性,永远都是线程安全的)
目前锁有三种:synchronized / volatile / Lock
三类锁各有所长,本节先介绍关键字 :synchronized
synchronized关键字用来实现线程之间同步互斥。
public classTest{private num = 0;public voidaddId(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){
e.printStackTrace();
}
}
}public class ThreadA extendsThread {privateTest test;publicThreadA(Test test){this.test =test;
}
@Overridepublic voidrun(){super.run(); //在笔记 (一) 里面提到过,其实觉得可以不加上
test.addId("a");
}
}public class ThreadB extendsThread {privateTest test;publicThreadB(Test test){this.test =test;
}
@Overridepublic voidrun(){super.run(); //在笔记 (一) 里面提到过,其实觉得可以不加上
test.addId("b");
}
}
同时运行时,Test类中的num变量会被两个线程不同步的修改,出现错误
public classRun{public static voidmain(String[] args){
Test test= newTess(); //!!!!!!!!!!这里很关键,这里是同一个实例对象test!下文会提到!
ThreadA athread= newThreadA(test);
athread.start();
ThreadB bthread= newThreadB(test);
bthread.start();
}
}
这时,想让他们同步的办法便是给他们的 addId() 方法,加上锁:synchronized 关键字。
synchronized public voidaddId(String username){//...中间部分全部相同的代码
}
结论:两个线程访问同一个对象中的同步方法时,一定是线程安全的。
既然有同一个对象中的同步方法,肯定就会有多个对象的情况,这个时候就会有多个对象多个锁的情况:
这里详细说一下synchronized关键字加锁的范围:(本部分加锁范围借鉴了宇学愈多的博文)
修饰普通方法(锁住的是当前实例对象)
同步代码块传参this(锁住的是当前实例对象)
同步代码块传参变量对象 (锁住的是变量对象)
同步代码块传参class对象(全局锁)
修饰静态方法(全局锁)
构造函数,原型对象,实例对象三者之间的关系
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
Example e =new Example(n); //构造函数。
通过调用构造函数产生的实例对象,都拥有一个内部属性,指向了原型对象。其实例对象能够访问原型对象上的所有属性和方法。//e 为实例对象
1 修饰普通方法:
public classSynchronizedTest {//锁住了本类的实例对象
public synchronized voidtest1() {try{
logger.info(Thread.currentThread().getName()+ " test1 进入了同步方法");
Thread.sleep(5000);
logger.info(Thread.currentThread().getName()+ " test1 休眠结束");
}catch(InterruptedException e) {
e.printStackTrace();
}
}public static voidmain(String[] args) {
SynchronizedTest st= newSynchronizedTest();
SynchronizedTest st2= newSynchronizedTest();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test1();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st2.test1();
}).start();
}
}
本例的实例对象为 st st2 :
同一个实例调用会阻塞(开篇提到的例子中,两个线程访问同一个对象实例方法,所以会产生阻塞)
不同实例调用不会阻塞
上文的代码的运行结果是没有阻塞的,因为是不同的实例对象,调用了相同的方法 test1() .
2 同步代码块穿参this
同一个实例调用会阻塞
不同实例调用不会阻塞
public classSynchronizedTest {//锁住了本类的实例对象
public voidtest2() {synchronized (this) {try{
logger.info(Thread.currentThread().getName()+ " test2 进入了同步块");
Thread.sleep(5000);
logger.info(Thread.currentThread().getName()+ " test2 休眠结束");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}public static voidmain(String[] args) {
SynchronizedTest st= newSynchronizedTest();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test2();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test2();
}).start();
}
}
和 1 一样,同样是锁住了当前的实例对象
3 同步代码块传参变量对象
同一个属性对象才会实现同步
public classSynchronizedTest {publicInteger lockObject;publicSynchronizedTest(Integer lockObject) {this.lockObject =lockObject;
}//锁住了实例中的成员变量
public voidtest3() {synchronized(lockObject) {try{
logger.info(Thread.currentThread().getName()+ " test3 进入了同步块");
Thread.sleep(5000);
logger.info(Thread.currentThread().getName()+ " test3 休眠结束");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}public static voidmain(String[] args) {
SynchronizedTest st= new SynchronizedTest(127);
SynchronizedTest st2= new SynchronizedTest(127);new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test3();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st2.test3();
}).start();
}
}
同一个实例对象的成员属性肯定是同一个,此处列举的是不同实例的情况,但是 依旧实现了同步,原因如下:
Integer存在静态缓存,范围是-128 ~ 127,当使用Integer A = 127 或者 Integer A = Integer.valueOf(127) 这样的形式,都是从此缓存拿。如果使用 Integer A = new Integer(127),每次都是一个新的对象。此例中,两个对象实例的成员变量 lockObject 其实是同一个对象,因此实现了同步。还有字符串常量池也要注意。所以此处关注是,同步代码块传参的对象是否是同一个。这跟第二个方式其实是同一种。
4、同步代码块传参class对象(全局锁)
所有调用该方法的线程都会实现同步。
public classSynchronizedTest {//全局锁,类是全局唯一的
public voidtest4() {synchronized (SynchronizedTest.class) {try{
logger.info(Thread.currentThread().getName()+ " test4 进入了同步块");
Thread.sleep(5000);
logger.info(Thread.currentThread().getName()+ " test4 休眠结束");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}public static voidmain(String[] args) {
SynchronizedTest st= newSynchronizedTest();
SynchronizedTest st2= newSynchronizedTest();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test4();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st2.test4();
}).start();
}
}
类锁,直接锁了全局了
5、修饰静态方法(全局锁)
所有调用该方法的线程都会实现同步
public classSynchronizedTest {//全局锁,静态方法全局唯一的
public synchronized static voidtest5() {try{
logger.info(Thread.currentThread().getName()+ " test5 进入同步方法");
Thread.sleep(5000);
logger.info(Thread.currentThread().getName()+ " test5 休眠结束");
}catch(InterruptedException e) {
e.printStackTrace();
}
}public static voidmain(String[] args) {
SynchronizedTest st= newSynchronizedTest();
SynchronizedTest st2= newSynchronizedTest();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st.test5();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
st2.test5();
}).start();new Thread(() ->{
logger.info(Thread.currentThread().getName()+ " test 准备进入");
SynchronizedTest.test5();
}).start();
}
}
结论:synchronized在语法维度上主要分为三个用法
静态方法加上关键字
实例方法(也就是普通方法)加上关键字
方法中使用同步代码块
前两种方式最为偷懒,第三种方式比前两种性能要好。
本篇的最后加上一个多线程的题目:利用5个线程并发执行,num数字累计计数到10000,并打印。
/*** Description:
* 利用5个线程并发执行,num数字累加计数到10000,并打印。
* 2019-06-13
* Created with OKevin.*/
public classCount {private int num = 0;public static void main(String[] args) throwsInterruptedException {
Count count= newCount();
Thread thread1= new Thread(count.newMyThread());
Thread thread2= new Thread(count.newMyThread());
Thread thread3= new Thread(count.newMyThread());
Thread thread4= new Thread(count.newMyThread());
Thread thread5= new Thread(count.newMyThread());
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread1.join();
thread2.join();
thread3.join();
thread4.join();
thread5.join();
System.out.println(count.num);
}private synchronized voidincrese() {for (int i = 0; i < 2000; i++) {
num++;
}
}class MyThread implementsRunnable {
@Overridepublic voidrun() {
increse();
}
}
}
java丐帮_java多线程学习笔记(三)相关推荐
- java丐帮_java多线程学习笔记(五)
补充一个synchronized关键字的结论: 线程A先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法 A线程现持有object对 ...
- java丐帮_java多线程学习笔记(四)
上一节讲到Synchronized关键字,synchronized上锁的区域:对象锁=方法锁/类锁 本节补充介绍一下synchronized锁重入: 关键字synchronized拥有锁重入的功能,也 ...
- java丐帮_Java多线程学习笔记(一)
一.什么是多线程 首先是多线程的概念: 多线程是异步的,和单任务不同,并不一定按照代码的执行顺序(上图左)来运行,而是交错占用CPU运行(上图右): 二.如何使用多线程 JAVA多线程有两种实现方式: ...
- java丐帮_java多线程学习笔记(二)
上一节讲到多线程的创建,两种创建方式一种继承Thread类,一种实现Runnable接口: 常用的多线程函数: currentThread()方法 返回代码段正在被哪个线程调用的信息 i ...
- java丐帮_java多线程学习笔记(八)
本节开始线程间通信: 使用wait/notify实现线程间通信 生产者/消费者模式的实现 方法join的使用 ThreadLocal类的使用 可以通过使用 sleep() 结合 while(true) ...
- java丐帮_java多线程学习笔记(六)
本节开始synchronized关键字的最后一部分内容: 静态同步方法synchronized方法与synchronized(class)代码块 关键字synchronized还可以用在static静 ...
- java线程集合点_Java多线程学习笔记(三) 甚欢篇
使人有乍交之欢,不若使其无久处之厌 <小窗幽记>很多时候,我们需要的都不是再多一个线程,我们需要的线程是许多个,我们需要让他们配合.同时我们还有一个愿望就是复用线程,就是将线程当做一个工人 ...
- java 编程思想 多线程学习笔记
java 编程思想 多线程学习笔记 一.如何创建多线程? 1.继承 java.lang.Thread 类 2.实现 java.lang.Runnable 接口 3.Callable接口 总之,在任何线 ...
- Java多线程学习笔记(三)休眠(sleep),让步(yield),插队(join)
线程目录 线程休眠: 线程让步: 线程插队: 线程休眠: 使用Thread.sleep(long millis)可以使线程休眠,也就是将正在执行的线程暂停,将CPU让给其他线程去执行. 实例: pub ...
最新文章
- 为什么做了梦第二天想不起来_为什么做的梦总是想不起来?科学的解释是……...
- Qt学习笔记之SQLITE数据库
- Google Gson 使用简介
- java uuid_Java UUID
- oracle sequence last_number,关于oracle序列的LAST_NUMBER
- 实战:kafka实现日志收集系统
- 如何快速打好java基础_学习Java课程时如何才能打好基础呢?
- ERROR streaming.StreamExecution:createConsumer(ConsumerStrategy.scala:63)
- cbitmap 从内存中加载jpg_Pytorch数据加载的分析
- 可控硅型号怎样识别_可控硅是什么_可控硅型号_可控硅分类及判别_可控硅种类...
- Hello China操作系统的安装和使用
- js中 json对象与json字符串相互转换的几种方式
- 像素值/DN值/数字量化值
- GEF原理及实现系列(二、模型)
- 在c的基础上关于c#入门的一些个人理解
- 让人感到心灵平静,阳光温暖的图片
- 世界生产力科学院院士(中国籍)名单
- window10安装Mac虚拟机详细教程
- 零基础入门金融风控-贷款违约预测-机器学习-数据分析
- 创建PostgreSQL数据库