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

java基础篇——多线程

2015-05-29 20:24 579 查看
多线程

一 引言 :

进程是一个正在执行中的程序,每个进程执行都有一个执行顺序,或执行路径;
线程是进程中的内容,是进程中独立的控制单元;
线程控制着进程的执行,或者是一个控制单元;
一个进程中至少有一个线程;

二 如何创建线程

1.第一种方式为:

将类声明为Thread的子类,该子类应重写Thread类的run方法

例如:

<pre name="code" class="java">class Demo extends Thread
{
@Override
public void run() {
super.run();
}
}


class Test { public static void main(String[] args) { Demo d = new Demo(); d . start(); }}


2. 第二种方式是实现 Runnable接口,并重写Thread的run方法



class  RunnableDemo implements Runnable{
@Override
public void run() {
}
}

class TestRunnableClass{
public static void main(String[] args)
{
RunnableDemo  runnableDemo  = new  RunnableDemo();
Thread  t = new  Thread(runnableDemo);
t . start();
}
}


注: 1. 定义类实现Runable接口来创建线程
2. 覆盖 Runable 中run方法,将要执行的代码放入;
3. 通过 Thread 类建立线和对象
4.将 Runnable接口子类对象作为实际参数传递给Thread类的构造函数中
5.调用 Thread类的 start 方法 开户线程
注: 两种创建线程方式的区别:
实现方式的好处,了单继承的局限性
在定义线程时,建议使用实现类
继承Thread: 线程代码存放在Thread类的run方法中
实现Runable,线程将代码存放在接口的子类run方法中

3.注 : 在某一个时刻,只能有一个程序在运行;
在多线程中,有一个特性:随机执行;
d . run();是调用类Demo类中的run方法,而未启动线程

4.为什么要重写run方法?

将自定义代码存储在run方法中,让线程运行;
例:

class ThreadDemos1 extends Thread
{
public void run()
{
for(int x =0;x<10;x++)
{
System.out.println(this.getName()+" " + x);
}
}
}

//其中   this.getName  是获得线程的名称

class ThreadDemos2 extends Thread
{
ThreadDemos2(String name)
{
super(name);
}
public void run()
{
for(int x =0;x<10;x++)
{System.out.println(this.getName()+" " + x);
}
}
}


建立测试类
public class TestDemo {
public static void main(String[] args) {
ThreadDemos1  d1 = new ThreadDemos1();
d1.start();
ThreadDemos2  d = new ThreadDemos2("one");
d.start();
}
}


三 多线程运行状态

阻塞状态

|

线程建立——(start())——运行————sleep()————冻结

|

stop

四 自定义线程名称:

** 线程都有自己的名称 : Thread-标号 ,, 标号是从0开始的;
** 获得线程名称的方法 : this.getName(); Thread.currentThread().getName()
** 可以在线程初始化始时对线程命名;

1.在线程初始化时候对其命名

class Demoss extends Thread
{
public void run()
{
for(int x =0;x<10;x++)
{
System.out.println(this.getName()+" " + x);
}
}
}

public class TestDemo {

public static void main(String[] args) {

Demoss  d1 = new Demoss("one");
d1.start();

}
}


2. 可以使用构造函数对其重命名;

class Demos extends Thread
{
Demos(String name)
{
super(name);
}
public void run()
{
for(int x =0;x<10;x++)
{
System.out.println(this.getName()+" " + x);
}
}
}

public class TestDemo {

public static void main(String[] args) {

Demos  d1 = new Demos("one");
d1.start();

}
}


五 多线程的安全问题

1.同步 代码块

java对于多线程的安全问题,提供了专业的解决方式,就是 使用同步代码块,
结构

synchronized(对象)

{

被同步的代码

}


例如一个售票例子
总共1000张票 四个售票机
package test;
public class TestDeoo {

public static void main(String[] args) {

Ticket  t = new Ticket();

Thread  t1 = new Thread(t);
Thread  t2 = new Thread(t);
Thread  t3 = new Thread(t);
Thread  t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}

class Ticket implements Runnable{
private int tick=1000;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tick>0){
"                                 try{Thread.sleep(10);}catch(Exception e){ };
}      <pre name="code" class="java">


System.out.println(Thread.currentThread().getName()+"sale"+tick--); } } }}


2.同步代码块原理:

例如有两个线程,当线程0获得CPU的执行权后,会读到synchronized代码块, 判断,然后会获得进入许可,继续执行下面的代码,随后synchronized 关闭入口,读到 stop时,线程结束,此时入口开放,等待下一线程的进入执行。。。。。。。。。。

3.使用同步代码块的前提

<1> 必须有两个或者两个以上的线程
<2> 必须是多个线程使用同一个锁

4.同步代码块的好处

解决了线程的安全问题

5.同步代码块的弊端

多个线程要判断锁,较为消耗资源

六 同步函数




public class StringDemo {

public static void main(String[] args) {
Cuss c = new Cuss();
Thread t = new Thread(c);
Thread t2 = new Thread(c);
t.start();
t2.start();
}
}

class Bank {
private int sum;
Object obj = new Object();

public void add(int x) {
synchronized (obj) {
sum = sum + x;
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println("sum " + sum);
}
}
}

class Cuss implements Runnable {
private Bank b = new Bank();

public void run() {
for (int x = 0; x < 3; x++) {
b.add(100);
System.out.println(Thread.currentThread().getName());
}
}
}


使用同步函数形式来写出上述问题
class Banks {
private int sum;
Object obj = new Object();

public synchronized void add(int x) {

sum = sum + x;
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println("sum " + sum);

}
}

class Cuss implements Runnable

{
private Banks b = new Banks();

public void run() {
for (int x = 0; x < 3; x++) {
b.add(100);
System.out.println(Thread.currentThread().getName());
}
}
}

public class stringDemo {

public static void main(String[] args) {
Cuss c = new Cuss();
Thread t = new Thread(c);
Thread t2 = new Thread(c);
t.start();
t2.start();
}
}


七 单例设计模式

1. 饿汉式

class Single {
private static final Single s = new Singles();
private Single() {
}
public static Single getInstanceP
{
return s;
}
}


2. 懒汉式

class Single {

private static Single s = null;

private Single() {
}

public static Single getInstance() {

if (s == null) {

synchronized (Single.class) {

if (s == null) {
s = new Single();
}
}
}
return s;
}
}


线程间的通讯

线程间的的通讯,其实就是多个线程在操作同一个资源;但是操作的动作不同的资源

新建一个资源:

class Res
{
String name;
String sex;

}


创建一个输入线程类: 输入与输出线程要操作同一个资源

class Input implements Runnable
{
Res  r;
Input(Res r)
{
this.r=r;
}
public void run()
{
boolean b =true;
while(true)
{
if(b)
{
r.name="lishi";
r.sex="man";
b = false;
}
else
{
r.name="李四";
r.sex="男";
b = true;
}
}
}
}


新建一个输出线程: 输入线程与输出线程操作的是同一个资源

class Output implements Runnable
{
Res  r;

Output(Res r)
{
this.r=r;
}

public void run()
{
while(true)
{
System.out.println(r.name);
}
}
}


新建一个测试类

public class TestDemo {

public static void main(String[] args)

{

Res  r = new Res();
Input in = new Input(r);
Output out = new Output(r);

Thread t = new Thread(in );
Thread tl = new Thread(out);
t.start();
tl.start();
}
}


这个程序的输出结果应该为 lishi man

李四 男

。。。。。

测试一下,会出现乱码现象,如果要修改安全问题,就应该使用同步代码块

class Res
{
String name;
String sex;

}
class Input implements Runnable
{
Res r;
Input(Res r)
{
this.r=r;
}
public void run()
{
boolean b =true;
while(true)
{
synchronized(r)
{
if(b)

{
r.name="lishi";
r.sex="man";
b = false;
}
else
{
r.name="李四";
r.sex="男";
b = true;
}
}
}
}
}


class Output implements Runnable
{
Res  r;

Output(Res r)
{
this.r=r;
}

public void run()
{

synchronized(r)
{
while(true)
{

System.out.println(r.name+"  "+r.sex);
}
}
}
}


这样的话就解决了线程间的安全问题了

线程间的等待唤醒机制

新建一个资源

class Res
{
String name;
String sex;
boolean bo =  false;

}


class Input implements Runnable
{
Res  r;
Input(Res r)
{
this.r=r;
}
public void run()
{
boolean b =true;
while(true)
{
synchronized(r)
{
if(r.bo)
try{r.wait();}catch(Exception e){}
if(b)
{
r.name="lishi";
r.sex="man";
b = false;
}
else
{
r.name="李四";
r.sex="男";
b = true;
}
r.bo =true ;
r.notify();
}
}
}
}


class Output implements Runnable
{
Res  r;

Output(Res r)
{
this.r=r;
}

public void run()
{
while(true)
{
synchronized(r)
{
if(!r.bo)
try{r.wait();}catch(Exception e){}
System.out.println(r.name+"  "+r.sex);
r.bo=false;
r.notify();
}
}
}
}


public class StringDemo {

public static void main(String[] args)

{
Res  r = new Res();
Input in = new Input(r);
Output out = new Output(r);

Thread t = new Thread(in );
Thread tl = new Thread(out);
t.start();
tl.start();

}
}


输出结果为

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

lishi man

李四 男

注 : wait :

notify()

notifyAll()

都使用在同步中,因为要对持有监视器的线程操作,所以要使用在同步中,因为只有 同步才具有锁;

为什么这些操作线程的方法要定义在object 类中呢?

因为这些方法在操作同步线程中,都必须要标识它们所操作线程的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中

优化后的代码

public class TestDemo {

public static void main(String[] args) {
Resi  r = new Resi();
new Thread(new Inputi(r)).start();
new  Thread(new  Outputi(r)).start();
}
}
class Resi
{
String name;
String sex;
boolean bo =  false;
public synchronized void set(String name ,String sex)
{
if(bo)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
bo = true ;
this.notify();
}
public synchronized void out()
{
if(!bo)
try{this.wait();}catch(Exception e){}
System.out.println(name +"  "+sex);
bo = false;
this.notify();
}
}

class Inputi implements Runnable
{
Resi  r;
Inputi(Resi r)
{
this.r = r;
}

public void run()
{
boolean b =true;
while(true)
{
if(b)
{
r.set("lishi","man");
b = false;
}
else
{
r.set("李四","男");
b = true;
}
}
}
}

class Outputi implements Runnable
{
Resi  r;
Outputi(Resi r)
{
this.r = r;
}
public void run()
{
while(true)
r.out();
}


生产者与消费者 线程间的通信

public class StringDemo {

public static void main(String[] args) {
Resourced r = new Resourced();
new Thread(new Producerd(r)).start();//1
new Thread(new Producerd(r)).start();//2
new Thread(new Consumerd(r)).start();//3
new Thread(new Consumerd(r)).start();//4
}
}


/*
* 生产一个,消费一个*/
class Resourced
{
private String name;
private int count  = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag)
try{wait();}catch(Exception e){}
this.name = name+"  "+count++;
System.out.println(Thread.currentThread().getName()+"生产者,,,," + this.name);
flag = true;
this.notifyAll();
}

public void out(){

while(!flag)
try{wait();}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"消费者    " + this.name);
flag = false;
this.notifyAll();
}
}


class Producerd implements Runnable
{
private Resourced res ;
Producerd(Resourced res)
{
this.res = res;
}
public void run()
{
while(true)
'7B
res.set("商品");
}
}
}


class Consumerd implements Runnable
{
private Resourced res;
Consumerd(Resourced res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: