java线程互斥的实现_Java线程(一):传统线程的实现、互斥与通信

一、实现多线程:

在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

1、继承Thread类

/**

*

* TraditionThread.java

*

* @title 继承Thread

* @description

* @author SAM-SHO

* @Date 2014-11-8

*/

class ExThread extends Thread {

@Override

public void run() {

while (true) {

try {

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("0:" + Thread.currentThread().getName());

}

}

}

2、实现Runable接口 ——建议使用接口

/**

*

* TraditionThread.java

*

* @title 实现 Runnable 接口

* @description

* @author SAM-SHO

* @Date 2014-11-8

*/

class RunThread implements Runnable {

@Override

public void run() {

while (true) {

try {

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("1:" + Thread.currentThread().getName());

}

}

}

运行:一个Thread对象就有一个线程

// 线程一

ExThread tThread1 = new ExThread();

tThread1.start();

// 线程二

Thread tThread2 = new Thread(new RunThread());

tThread2.start();

【结果】:

0:Thread-0

1:Thread-1

0:Thread-0

1:Thread-1

0:Thread-0

1:Thread-1

.......

二、线程互斥:synchronized

1、概述:

1)、线程安全问题,简单理解可以用银行转账来解释。

2)、传统线程互斥,使用 synchronized 关键字。

3)、synchronized() 代码块:

①、synchronized(对象) 代码块需要锁定同一个对象,一般会锁定业务类对象,即synchronized(this)即可。

②、如静态方法互斥,则需要锁定内存中的字节码对象,即synchronized(XXX.class)。

③、synchronized 方法锁定的也是对象。

4)、synchronized 方法:一个方法只有一个synchronized ,不然容易产生死锁。

2、实现

注意点一:静态方法不能创建内部类实例对象

1)、原因:

①、内部类可以访问外部类的成员变量;

②、成员变量是在对象创建后才分配空间的;

③、静态方法中可以不创建对象即访问对象的方法。

2)、实例分析:Outputer是内部类,可以访问外部类的成员变量,即一定有了外部类的实例对象,但是在main方法中根本不需要创建外部类对象,就矛盾了。

注意点二:线程调用的必须是同一个对象。

1)、如线程中每次都new Outputer()然后再去调用方法,那么synchronized(this) 和synchronized 方法是实现不了互斥的。

2)、但是这种情况下 锁定字节码是可以是实现的。其实由于字节码的唯一性,任何时候锁定字节码都是可以实现互斥的。

3)、具体使用哪一种需要根据实际对象,但是一般我们不推荐锁定字节码而是使用同一个对象。

互斥代码如下:线程方法在通信中得到改善。

package com.Thread;

/**

*

* TraditionalThreadSynchronized.java

*

* @title 传统线程互斥

* @description

* @author SAM-SHO

* @Date 2014-8-17

*/

public class TraditionalThreadSynchronized {

/**

* @param args

*/

public static void main(String[] args) {

// 静态方法不能创建内部类实例对象

// 原因: 内部类可以访问外部类的成员变量;成员变量是在对象创建后才分配空间的;静态方法中可以不创建对象即访问对象的方法。

// 如

// Outputer是内部类,可以访问外部类的成员变量,即一定有了外部类的实例对象,但是在main方法中根本不需要创建外部类对象,就矛盾了。

// FirstThread c = new FirstThread();

TraditionalThreadSynchronized tThreadSynchronized = new TraditionalThreadSynchronized();

tThreadSynchronized.init();

}

/**

* 定义初始化方法, 解决main(静态)方法不能创建内部类实例对象

*/

public void init() {

// 线程一

FirstThread firstThread = new FirstThread();

firstThread.start();

// 线程二

SecondThread secondThread = new SecondThread();

secondThread.start();

}

/*

* 线程一

*/

class FirstThread extends Thread {

Outputer out = new Outputer();//new 以后就是两个对象,使用this是锁不住的

@Override

public void run() {

while (true) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

out.output2("ShaoXiaoBao");

}

}

}

/*

* 线程二

*/

class SecondThread extends Thread {

Outputer out = new Outputer();

@Override

public void run() {

while (true) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

out.output2("ZhaoXiaoNiu");

}

}

}

/**

*

* Outputer.java

*

* @title 内部类实现输出,业务类

* @description output方法要实现原子性

*

* @author SAM-SHO

* @Date 2014-8-17

*/

class Outputer {

// 使用synchronized 代码块

// synchronized(对象) 代码块需要锁定同一个对象,

// 这边如果synchronized (name)就不行,一定要 Outputer对象

// output() 和 output1() 方法也是互斥的

public void output(String name) {

int len = name.length();

synchronized (this) {//this就是Outputer这个对象

for (int i = 0; i < len; i++) {

System.out.print(name.charAt(i));

}

System.out.println();

}

}

/**

* synchronized 方法

* 其实锁定的也是Outputer 对象

* @param name

*/

public synchronized void output1(String name) {

int len = name.length();

for (int i = 0; i < len; i++) {

System.out.print(name.charAt(i));

}

System.out.println();

}

/**

* 如果有静态方法 a(),output2()与之需要互斥,

* 则可以锁定该类字节码

* @param name

*/

public void output2(String name) {

int len = name.length();

synchronized (Outputer.class) {// 解决与静态方法的互斥,使用字节码对象

for (int i = 0; i < len; i++) {

System.out.print(name.charAt(i));

}

System.out.println();

}

}

}

}

三、线程通信

1、关键字:wait、notify、一个boolean类型参数。

2、通过一个简单的案例实现线程通信:两个线程,子线程循环10次,主线程循环30次,然后子线程又循环10次,主线程30次,如此一共循环20次。

3、线程同步与互斥: 互斥、同步不是写在线程上,而是写在线程访问的资源上。

4、使用while()代替if(),这样会多检查一次参数,更加健壮。

5、synchronized(对象) 与 对象.wait 必须为同一个,不然会报错。

线程同步实现的代码如下:

package com.Thread;

/**

*

* TranditionalThreadComunication.java

*

* @title 线程同步与互斥: 互斥、同步不是写在线程上,而是写在线程访问的资源上。

* @description

* @author SAM-SHO

* @Date 2014-8-17

*/

public class TranditionalThreadComunication {

/**

* @param args

*/

public static void main(String[] args) {

TranditionalThreadComunication tThreadComunication = new TranditionalThreadComunication();

tThreadComunication.init();

}

/**

* 定义初始化方法,

* 解决main(静态)方法不能创建内部类实例对象

*/

public void init() {

//保证两个线程访问同一个资源对象

Bussiness tBussiness = new Bussiness();

ChildThread childThread = new ChildThread(tBussiness);//子线程

MainThread mainThread = new MainThread(tBussiness);//主线程

childThread.start();

mainThread.start();

}

/*

* 子线程

*/

class ChildThread extends Thread {

private Bussiness tBussiness;

public ChildThread(Bussiness tBussiness) {

this.tBussiness = tBussiness;

}

@Override

public void run() {

for (int i = 0; i <= 20; i++) {

tBussiness.eachChild(i);

}

}

}

/*

* 主线程

*/

class MainThread extends Thread {

private Bussiness tBussiness;

public MainThread(Bussiness tBussiness) {

this.tBussiness = tBussiness;

}

@Override

public void run() {

for (int i = 0; i <= 20; i++) {

tBussiness.eachMain(i);

}

}

}

/**

*

* Bussiness

*

* @title 业务实现类

* @description 用到的共同数据的若干方法应该设计在一个类身上

*

* @author SAM-SHO

* @Date 2014-8-17

*/

class Bussiness {

private boolean bShouldSub = true;

/*

* 循环10次的方法

* if 可以用 while代替

*/

public synchronized void eachChild(int index) {

while(!bShouldSub) {//一开始是true,那么就是执行循环

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

for (int i = 0; i <= 10; i++) {

System.out.println("子 线程-- "+i+ ", 第"+index+"循环");

}

bShouldSub = false;

this.notify();//唤醒主线程

}

/*

* 循环50次的方法

* if 可以用 while代替

*/

public synchronized void eachMain(int index) {

while(bShouldSub) {//一开始是true,那么就会wait

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

for (int i = 0; i <= 30; i++) {

System.out.println("主 线程---- "+i + ", 第"+index+"循环");

}

bShouldSub = true;

this.notify();//唤醒子线程

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值