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

java多线程

2015-07-31 13:18 531 查看
多线程

1.1 多线程的概念

进程:正在进行中的程序。

线程:进程中一个负责执行的控制单元。

解析:

1.一个进程中可以有多个线程,称之为多线程。

2.一个进程中至少有一个线程,称为主线程

3.开启多个线程是为了同时运行多部分代码,每个线程都有自己运行的内容,这个内容称为线程需要执行的任务。

多线程的好处:解决了多部分代码同时运行的问题。

多线程的弊端:线程太多会导致效率的降低。

程序执行特点:其实, 多个应用程序同时执行都是CPU在做着快速的切换完成的。 这个切换是随机的。 CPU的切换是需要花

费时间的, 从而导致了效率的降低。

1.2 线程创建于执行

创建的方式之一:继承Thread类

1.定义一个继承Thread类。

2.覆盖Thread类中的run方法。

3.直接创建Thread的子类对象创建线程。

4.调用start方法开启线程并调用线程的run方法。

thread中的几种常见方法:

1.Thread.currentThread():获取当前现成的对象。

2.t.getName();获取t线程对象的名称。格式:Thread-编号(从0开始)

示例:

[java] view plaincopy

class Demo extends Thread{

String name ;

Demo(String name){

this.name = name;

}

public void run(){

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

System.out.println(name+"... x="+x+"... ThreadName="+Thread.currentThread().getName());

}

}

}

class ThreadDemo{

public static void main(String[] args){

Demo d1 = new Demo("旺财");

Demo d2 = new Demo("小黄");

d1.start();

d2.start();

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

System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());

}

}

}

运行结果:

发现三个输出语句无序的打印出来。

创建的方式之二:实现Runnable接口

1.定义一个类实现Runnable接口。

2.覆盖接口中的run方法,将线程将要执行的代码放入其中。

3.通过Thread类创建对象,并把实现了Runnable的子类对象作为参数传给Thread类的构造函数。

4.调用Thread中的start方法,开启线程。

实现Runnable的好处:

1.将线程的任务从线程子类中分离出来,进行了单独的封装,安装面向对象的思想将任务封装成了对象。

2.避免了java单继承的局限性。

3.实现Runnable方法更为常见。

示例:

[java] view plaincopy

class Demo implements Runnable{

public void run(){

show();

}

public void show(){

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

System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());

}

}

}

class ThreadDemo{

public static void main(String[] args){

Demo d1 = new Demo();

Thread t1 = new Thread(d1);

Thread t2 = new Thread(d1);

t1.start();//可简写为new Thread(d1).start();

t2.start();

}

}

运行结果:两个线程无序的把0-9依次打印出来。

1.3 线程安全性问题。

举例说明问题:

需求:模拟4个窗口卖票的问题。

代码:

[java] view plaincopy

class Ticket implements Runnable{

private int num = 100;

public void run(){

while(true){

if(num>0){

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

}

}

}

}

class TicketDemo{

public static void main(String[] args){

Ticket t = new Ticket();

new Thread(t).start();

new Thread(t).start();

new Thread(t).start();

new Thread(t).start();

}

}

运行结果:

发现运行出现两个99和出现0,-1的情况。

分析:

因为cpu执行多线程时,是在多线程之间快速切换的,当线程-0执行到num=1但为对其自减就停止了,此时线程-1进来了num没有变化,输出后num自减num=0,回到线程-0,输出的就是num=0了。

线程安全问题的产生:

1.多个线程同时操作共享数据时。

2.操作的共享数据的代码有多条。

1.4 线程安全问题的解决方案

思路:将多个线程执行的代码封装在一个空间内,一次只让一个线程执行其中的代码。

方法:使用synchronized(同步)

格式:

synchronized(对象){

需要被同步的代码,即多线程执行的代码;

}

同步的好处:解决了多线程的安全性问题。

同步的弊端:当线程相当多时,同一时间只有一个线程在执行,降低了运行的效率。

同步的前提:必须有多个线程并使用同一个锁。

卖票修改后代码:

[java] view plaincopy

class Ticket implements Runnable{

private int num = 100;

Object obj = new Object;//同步需要锁,锁为一个对象或者一个class文件,这里用Object对象。

public void run(){

while(true){

synchronized(obj){

if(num>0){

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

}

}

}

}

}

利用同步带代码块解决安全性问题:

需求:储户,两个,每个都到银行存钱,每次存入100,共存三次。

[java] view plaincopy

class Bank{

private int sum;

public void add(int num){

synchronizd(this){

sum = sum + num;

System.out.println("sum = "+sum);

}

}

}

class Cus implements Runnable{

private Bank b = new Bank();

public void run(){

for(int x = 1;x<=3;x++){

d.add(100);

}

}

}

class BankDemo{

public static void main(String[] args){

Cus c = new Cus();

new Thread(c).strat();

new Thread(c).strat();

}

}

运行结果:

sum = 100

sum = 200

sum = 300

sum = 400

sum = 500

sum = 600

synchronized可作为修饰符放在函数中,称为同步函数。

格式:

public synchronized void add(int num){

同步代码;

}

同步代码和同步函数的区别:同步代码的锁为自定义的,而同步函数的锁为固定的this。建议使用同步代码块。

静态的同步函数使用的锁是干函数所属字节码文件对象,可以用 对象.getClass() 方法获取,也可以用 类.class() 表示,还可通过 Class.forName(类名) 获取。

1.5 死锁示例

一般出现在同步嵌套中,即同步中有同步,并且锁用的不同。

示例

[java] view plaincopy

class DieLock implement Runnable{

Object obja = new Object();

Object objb = new Object();

boolean flag = true;

public void run(){

if(flag){

synchroinzed(obja){

System.out.println("hellow obja");

synchroinzed(objb){

System.out.println("hellow objb");

}

}

}

else{

synchroinzed(objb){

System.out.println("hellow objb");

synchroinzed(obja){

System.out.println("hellow obja");

}

}

}

}

class Demo{

public static void main(String[] args){

DieLock d = new DieLock();

new Thread(d).strat();

new Thread(d).strat();

}

}

死锁运行后出现,光标停在一个位置不动。

1.6 线程间的通讯

当多个线程对同一资源进行操作,而操作的方法却不同时,就需要多线程通讯。

等待/唤醒机制涉及的方法:

1.wait():让线程处于冻结状态,被wait()的线程会被存储到线程池中。

2.notify():唤醒线程池中的线程(按被冻结的顺序)。

3.notifyAll():唤醒线程池中所有的线程。

4.这些方法都被定义在object中。

注意点:

1.这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法。

2.必须要明确到底操作的是哪个锁上的线程。

3.wait和sleep的区别

1)wait可以指定时间也可以不指定时间,而sleep必须执行时间。

2)在同步中,对cup执行权和锁的处理

wait:释放执行权,释放锁。

sleep:释放执行权,不释放锁。

4.两种方法都涉及到 InterruptedException 需要处理

输入/输出 姓名/性别 例:

[java] view plaincopy

class Resource{

private String name;

private String sex;

boolean flag = false;

public synchronized viod set(String name ,String age){

if(flag){

try{

this.wait();

}

catch(InterruptedException e){

e.printStackTrace();

}

}

this.name = name ;

this.sex = sex;

flag = true;

this.notify();

}

public synchronized void out(){

if(!flag){

try{

this.wait();

}

catch(InterruptedException e){

e.printStackTrace();

}

}

System.out.println("name = "+name+"sex = "+sex);

flag = false;

this.notify();

}

}

class Input implements Runnable{

Resource r ;

Input(Resource r){

this.r = r;

}

public void run(){

int x = 0

while(true){

if(x==0)

r.set("张华","男");

if(x==1)

r.set("丽丽","女");

x=(x+1)%2;

}

}

}

class Output implement Runnable{

Resource r;

Out(Resource r){

this.r = r;

}

public void run(){

while(true){

r.out();

}

}

}

class ResourceDemo{

public static void main(String[] args){

Resource r = new Resource;

new Thread(new Input(r)).strat();

new Thread(new OutPut(r)).strat();

}

}

运行结果:

name = 张华sex =男

name = 丽丽sex =女

无序交替出现。

注意:当对资源的同一方法有多个线程在操作,判断用while,而且唤醒用notifyAll()。

1.7 JDK1.5特性(Lock)

Lock接口:出现代替同步代码块或者同步函数,将同步的隐式操作变成了显示操作。同时更为灵活,可以一个锁上加上多个监视器。

格式:

1.获得锁对象

Lock lock = new ReentrantLock();

lock.lock();

2.获得监视器(可获得多个监视器)

Condition con_p = lock.newCondition();

Condition con_c = lock.newCondition();

3.等待(有抛出异常)

con_p.await();

4.唤醒

con_p.signal();

5.释放锁(在finally语句中为一定要执行语句)

lock.unlock();

示例:

多个生产者、消费者

[java] view plaincopy

class Resource{

String name ;

int count = 1;

boolean flag = false;

Lock lock = new ReentrantLock();

Condition con_p = lock.newCondition();

Condition con_c = lock.newCondition();

public void set(String name){

lock.lock();

try{

while(flag){

try{

con_p.await();

}catch(InterruptedException e){

e.printStackTrace();

}

}

this.name = name + count;

count++

System.out.println(Thread.currentThread().getName()+"..生产.."+this.name);

flag = true;

con_c.signal();

}

finally{

lock.unlock();

}

}

public void out(){

lock.lock();

try{

while(!flag){

try{

con_c.await();

}catch(InterruptedException e){

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName()+"..消费.."+this.name);

flag = false;

con_p.signal();

}

finally{

lock.unlock();

}

}

}

class Producer implements Runnable{

Resource r;

Producer(Resource r){

this.r = r;

}

public void run(){

while(true){

r.set("烤鸭");

}

}

}

class Consumer implements Runnable{

Resource r;

Producer(Resource r){

this.r = r;

}

public void run(){

while(true){

r.out();

}

}

}

class ResourceDemo{

public static void main(String[] args){

Resource r = new Resource;

new Thread(new Producer(r)).strat();

new Thread(new Producer(r)).strat();

new Thread(new Consumer(r)).strat();

new Thread(new Consumer(r)).strat();

}

}

运行结果:

生产消费同一产品依次输出。

1.8 线程中其他方法:

1.停止线程:stop方法已经过时,怎么控制线程的结束,线程任务中都会有循环结构,只要控制循环就能结束任务。

2.t.interrupt():强制将处于冻结状态的t线程唤醒,但出现了InterruptedException异常需要处理。

3.t.setDeamon(true):守护线程,随着主线程的结束而结束。

4.t.join():抢夺执行权,等t线程运行完后在执行其他线程内容。

5.t.toString:返回线程的字符串表示形式:线程名称 优先级 线程组

6.t.setPriority(5):设置t线程的优先级(0-9),默认为5

7.t.yeild():停止t线程,执行其他线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: