一、关于Java多线程中的一些概念

1.1 线程基本概念

从JDK1.5开始,Java提供了3中方式来创建、启动多线程:

  方式一(不推荐)、通过继承Thread类来创建线程类,重写run()方法作为线程执行体;

  方式二、实现Runnable接口来创建线程类,重写run()方法作为线程执行体;

  方式三、实现Callable接口来创建线程类,重写run()方法作为线程执行体;

不同的是,其中方式一的效果最差,是因为

  1、线程类继承了Thread类,无法再继承其他父类;

  2、因为每条线程都是一个Thread子类的实例,因此多个线程之间共享数据比较麻烦。

二、一些关于synchronized关键字的简单Demo

2.1、多个线程单个锁

当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法里的代码首先会去尝试获得锁,如果拿到锁,执行synchronized代码体内容;

如果拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)

package com.sun.multithread.sync;

public class MyThread extends Thread {

    private int count = 5;

    // synchronized加锁
public synchronized void run() {
count--;
System.out.println(this.currentThread().getName() + " count = " + count);
} public static void main(String[] args) { MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread, "t1");
Thread t2 = new Thread(myThread, "t2");
Thread t3 = new Thread(myThread, "t3");
Thread t4 = new Thread(myThread, "t4");
Thread t5 = new Thread(myThread, "t5"); t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

2.2、多个线程多把锁

关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当成锁,所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁。但是在静态(static)方法上加synchronized关键字,表示锁定class类,类级别的锁
package com.sun.multithread.sync;

public class MultiThread {

    private static int num = 0;

    /**
* 在静态(static)方法上加synchronized关键字,表示锁定class类,类级别的锁
*
* 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当成锁,
* 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁
*
* @param tag 参数
*/
public static synchronized void printNum(String tag){
try {
if(tag.equals("a")){
num = 100;
System.out.println("tag a, set num over!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("tag b, set num over!");
}
System.out.println("tag " + tag + ", num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
// 两个不同的对象
final MultiThread m1 = new MultiThread();
final MultiThread m2 = new MultiThread(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
m1.printNum("a");
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
m1.printNum("b");
}
}); t1.start();
t2.start();
}
}

2.3 银行取钱的多线程例子

1、创建一个银行账户,并在里面写好取钱的方法,其中使用synchronized关键字修饰多线程操作的方法

package com.sun.multithread.sync.bankdemo;

/**
* 银行账户类
*
* @author ietree
*/
public class Account { /**
* 账号
*/
private String accountNo; /**
* 余额
*/
private double balance; public Account() {
} /**
* 带参构造函数
*
* @param accountNo 账户
* @param balance 余额
*/
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
} // 访问该账户的余额,使用synchronized修饰符将它变成同步方法
public synchronized double getBalance() {
return balance;
} /**
* 取钱的方法
*
* @param drawAmount 取钱金额
*/
public synchronized void draw(double drawAmount) { // 如果余额大于等于用户取的钱,则取款成功
if (balance >= drawAmount) {
// 取款成功
System.out.println(Thread.currentThread().getName() + "取钱成功!用户取出" + drawAmount + "元"); // 修改余额
balance -= drawAmount;
System.out.println("\t余额为: " + balance); } else { System.out.println(Thread.currentThread().getName() + "取钱失败!您的余额不足"); } } public String getAccountNo() {
return accountNo;
} public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
} // 重写hashCode()方法
public int hashCode() {
return accountNo.hashCode();
} // 重写equals()方法
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj.getClass() == Account.class) {
Account target = (Account) obj;
return target.accountNo.equals(accountNo);
}
return false;
} }

2、创建多个线程同时操作一个账户

package com.sun.multithread.sync.bankdemo;

class DrawThread extends Thread {

    /**
* 模拟用户账户
*/
private Account account; /**
* 当前取钱线程所希望取的钱数
*/
private double drawAmount; public DrawThread(String name, Account account, double drawAmount){
super(name);
this.account = account;
this.drawAmount = drawAmount;
} // 当多条线程修改同一个共享数据时,将涉及数据安全问题
public void run(){
account.draw(drawAmount);
}
} public class DrawTest { public static void main(String[] args) {
// 创建一个账户
Account acct = new Account("1234567", 1000); // 模拟两个线程对同一账户取钱
new DrawThread("路人甲", acct, 800).start();
new DrawThread("路人乙", acct, 800).start();
} }

2.4 业务整体需要使用完整的synchronized,保持业务的原子性

package com.ietree.multithread.sync;

/**
* 业务整体需要使用完整的synchronized,保持业务的原子性
*
* @author ietree
*/
public class DirtyRead { private String username = "Jack";
private String password = "123"; public synchronized void setValue(String username, String password) {
this.username = username; try {
// 睡眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} this.password = password; System.out.println("setValue最终结果:username = " + username + ", password = " + password);
} // synchronized
public void getValue() {
System.out.println("getValue方法得到:username = " + this.username + ", password = " + this.password);
} public static void main(String[] args) throws Exception { final DirtyRead dr = new DirtyRead();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("Dylan", "456");
}
});
t1.start();
Thread.sleep(1000); dr.getValue();
} }

程序输出:

getValue方法得到:username = Dylan, password = 123
setValue最终结果:username = Dylan, password = 456

这里出现了脏读现象,应该要保持业务的原子性,修改如下:

package com.ietree.multithread.sync;

/**
* 业务整体需要使用完整的synchronized,保持业务的原子性
*
* @author ietree
*/
public class DirtyRead { private String username = "Jack";
private String password = "123"; public synchronized void setValue(String username, String password) {
this.username = username; try {
// 睡眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} this.password = password; System.out.println("setValue最终结果:username = " + username + ", password = " + password);
} // synchronized
public synchronized void getValue() {
System.out.println("getValue方法得到:username = " + this.username + ", password = " + this.password);
} public static void main(String[] args) throws Exception { final DirtyRead dr = new DirtyRead();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("Dylan", "456");
}
});
t1.start();
Thread.sleep(1000); dr.getValue();
} }

程序输出:

setValue最终结果:username = Dylan, password = 456
getValue方法得到:username = Dylan, password = 456

当在set和get方法上同时使用了synchronized能确保业务原子性,不会出现脏读现象。

2.5 synchronized的重入

demo1:

package com.ietree.multithread.sync;

public class SyncDemo1 {

    public synchronized void method1(){
System.out.println("method1..");
method2();
}
public synchronized void method2(){
System.out.println("method2..");
method3();
}
public synchronized void method3(){
System.out.println("method3..");
} public static void main(String[] args) {
final SyncDemo1 sd = new SyncDemo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
} }

demo2:

package com.ietree.multithread.sync;

public class SyncDemo2 {

    static class Parent {
public int i = 10; public synchronized void operationSup() {
try {
i--;
System.out.println("Main print i = " + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} static class Sub extends Parent {
public synchronized void operationSub() {
try {
while (i > 0) {
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(100);
this.operationSup();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
}); t1.start();
}
}

2.6 synchronized的Exception

package com.ietree.multithread.sync;

public class SyncException {

    private int i = 0;

    public synchronized void operation() {
while (true) {
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if (i == 20) {
throw new RuntimeException();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
}, "t1");
t1.start();
}
}

2.7 锁对象的改变问题

demo1:

package com.ietree.multithread.sync;

public class ChangeLock {

    private String lock = "lock";

    private void method() {
synchronized (lock) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
lock = "change lock";
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final ChangeLock changeLock = new ChangeLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
}, "t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

程序输出:

当前线程 : t1开始
当前线程 : t2开始
当前线程 : t1结束
当前线程 : t2结束

demo2:

package com.ietree.multithread.sync;

public class ChangeLock {

    private String lock = "lock";

    private void method() {
synchronized (lock) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
// lock = "change lock";
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final ChangeLock changeLock = new ChangeLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
changeLock.method();
}
}, "t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

程序输出:

当前线程 : t1开始
当前线程 : t1结束
当前线程 : t2开始
当前线程 : t2结束

两个程序相比差异在于是否含有 lock = "change lock";这个改变了锁对象,所以输出有差异。

2.8 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况

package com.ietree.multithread.sync;

public class DeadLock implements Runnable {

    private String tag;
private static Object lock1 = new Object();
private static Object lock2 = new Object(); public void setTag(String tag) {
this.tag = tag;
} @Override
public void run() {
if (tag.equals("a")) {
synchronized (lock1) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
}
}
}
if (tag.equals("b")) {
synchronized (lock2) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行");
}
}
}
} public static void main(String[] args) { DeadLock d1 = new DeadLock();
d1.setTag("a");
DeadLock d2 = new DeadLock();
d2.setTag("b"); Thread t1 = new Thread(d1, "t1");
Thread t2 = new Thread(d2, "t2"); t1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

程序输出:

当前线程 : t1 进入lock1执行
当前线程 : t2 进入lock2执行

程序一直等待获取锁的状态,造成死锁问题。

2.9 同一对象属性的修改不会影响锁的情况

package com.ietree.multithread.sync;

public class ModifyLock {
private String name;
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public synchronized void changeAttributte(String name, int age) {
try {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始");
this.setName(name);
this.setAge(age); System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: " + this.getName() + ", "
+ this.getAge()); Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) { final ModifyLock modifyLock = new ModifyLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("Jack", 18);
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("Rose", 20);
}
}, "t2"); t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

程序输出:

当前线程 : t1 开始
当前线程 : t1 修改对象内容为: Jack, 18
当前线程 : t1 结束
当前线程 : t2 开始
当前线程 : t2 修改对象内容为: Rose, 20
当前线程 : t2 结束

2.10 使用synchronized代码块加锁,比较灵活

package com.ietree.multithread.sync;

public class ObjectLock {
public void method1() {
synchronized (this) { // 对象锁
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void method2() { // 类锁
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private Object lock = new Object(); public void method3() { // 任何对象锁
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
}); t1.start();
t2.start();
t3.start(); }
}

程序输出:

do method2..
do method3..
do method1..

以上语句是同时输出的,因为他们拿到的锁都是不同的锁,所以互不影响。

2.11 使用synchronized代码块减小锁的粒度,提高性能

package com.ietree.multithread.sync;

public class Optimize {

    public void doLongTimeTask() {
try {
System.out.println("当前线程开始:" + Thread.currentThread().getName() + ", 正在执行一个较长时间的业务操作,其内容不需要同步");
Thread.sleep(2000); synchronized (this) {
System.out.println("当前线程:" + Thread.currentThread().getName() + ", 执行同步代码块,对其同步变量进行操作");
Thread.sleep(1000);
}
System.out.println("当前线程结束:" + Thread.currentThread().getName() + ", 执行完毕"); } catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
final Optimize otz = new Optimize();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
}, "t2");
t1.start();
t2.start(); }
}

程序输出:

当前线程开始:t2, 正在执行一个较长时间的业务操作,其内容不需要同步
当前线程开始:t1, 正在执行一个较长时间的业务操作,其内容不需要同步
当前线程:t1, 执行同步代码块,对其同步变量进行操作
当前线程结束:t1, 执行完毕
当前线程:t2, 执行同步代码块,对其同步变量进行操作
当前线程结束:t2, 执行完毕

2.12 避免使用字符串常量锁

package com.ietree.multithread.sync;

public class StringLock {

    public void method() {
// new String("字符串常量")
synchronized ("字符串常量") {
try {
while (true) {
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
}, "t2"); t1.start();
t2.start();
}
}

程序输出:

当前线程 : t1开始
当前线程 : t1结束
当前线程 : t1开始
当前线程 : t1结束
当前线程 : t1开始
当前线程 : t1结束
当前线程 : t1开始
......

Java中的多线程Demo的更多相关文章

  1. java中的多线程 // 基础

    java 中的多线程 简介 进程 : 指正在运行的程序,并具有一定的独立能力,即 当硬盘中的程序进入到内存中运行时,就变成了一个进程 线程 : 是进程中的一个执行单元,负责当前程序的执行.线程就是CP ...

  2. Java 中传统多线程

    目录 Java 中传统多线程 线程初识 线程的概念 实现线程 线程的生命周期 常用API 线程同步 多线程共享数据的问题 线程同步及实现机制 线程间通讯 线程间通讯模型 线程中通讯的实现 @(目录) ...

  3. Java中使用多线程、curl及代理IP模拟post提交和get访问

    Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...

  4. 【转】Java中的多线程学习大总结

    多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...

  5. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  6. Java中的多线程=你只要看这一篇就够了

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  7. Android学习记录(5)—在java中学习多线程下载之断点续传②

    在上一节中我们学习了在java中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...

  8. Java中使用多线程、curl及代理IP模拟post提交和get訪问

    Java中使用多线程.curl及代理IP模拟post提交和get訪问 菜鸟,多线程好玩就写着玩.大神能够路过不吝赐教.小弟在这受教.谢谢! 很多其它分享请关注微信公众号:lvxing1788 ~~~~ ...

  9. Java中的多线程技术全面详解

    本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...

随机推荐

  1. 深入理解ajax系列第五篇——进度事件

    前面的话 一般地,使用readystatechange事件探测HTTP请求的完成.XHR2规范草案定义了进度事件Progress Events规范,XMLHttpRequest对象在请求的不同阶段触发 ...

  2. 计算两个YUV420P像素数据的PSNR---高等算法

    PSNR是最基本的视频质量评价方法.本程序中的函数可以对比两张YUV图片中亮度分量Y的PSNR.函数的代码如下所示. /** * Calculate PSNR between 2 YUV420P fi ...

  3. paoding-rose 了解

    paoding-rose 是人人开源的基于 spring 开发的 javaEE 框架.wiki 地址: https://code.google.com/archive/p/paoding-rose/ ...

  4. gzip 与 gunzip 语法与示例

    gzip 与 gunzip 语法与示例 语法: gunzip -c 被压缩的文件 > 已解压的文件示例: 将 catalina.out.gz 文件解压到 catalina.out 文件中: gu ...

  5. struct和typedef struct在c语言中的用法

    在c语言中,定义一个结构体要用typedef ,例如下面的示例代码,Stack sq:中的Stack就是struct Stack的别名. 如果没有用到typedef,例如定义 struct test1 ...

  6. appledoc导出iOS代码文档的使用和问题详解(干货篇)

    appledoc导出iOS代码文档的使用和问题详解(干货篇) 1. 简单说一下背景和自己感受 背景: 项目好像突然黄了,公司让详细写项目代码的注释并且导出文档,弄完之后就要封版. 说实话:听到这个消息 ...

  7. Java中Comparable和Comparator你知多少?

    前言: 我喜欢这种遨游在Java的世界里,精心研究学习新鲜事物的感觉,即便再小再细再微不足道的东西,也让我乐此不疲,同时我也更愿意将我所会的东西分享出来供大家学习以及方便自己日后回顾.好了,闲话不多说 ...

  8. curl中通过json格式吧post值返回到java中遇到中文乱码的问题

    首先是: curl中模拟http请求: curl -l 127.0.0.1:8080/spacobj/core/do?acid=100 -H "token:101hh" -H &q ...

  9. 关于window service2008系统iis部署访问证书,内部错误

    近期因为在做微信支付系列,做到退款的时候需要通过把数据流通过证书post过去的时候,win7.win8.xp部署在iis都没问题.但是部署到服务器 2008的时候就出现了内部错误. 折腾许久,总算找到 ...

  10. 进入效果 neon

    @-webkit-keyframes neon { 0% { opacity: .3; -webkit-transform: scale(2); transform: scale(2); } 100% ...