您的位置:首页 > 编程语言 > Java开发

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

2014-11-08 13:34 696 查看
一、实现多线程:

在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();//唤醒子线程
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: