码迷,mamicode.com
首页 > 编程语言 > 详细

多线程

时间:2014-08-29 17:57:38      阅读:347      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   os   使用   java   io   strong   

多线程

Java线程的实现

1)继承java.lang.Thread类,重写run()方法。(run()方法是线程体
2)定义实现java.lang.Runnable接口的类,实现run()方法。

可以使用一个线程类对象启动多个线程!多个线程对同一对象操作会相互影响。

线程状态转换(生命周期)

基本状态图

bubuko.com,布布扣

 

包含线程同步的状态转换图

bubuko.com,布布扣

包含线程通信的状态转换图

bubuko.com,布布扣

 三种阻塞状态

1)位于对象等待池中:当线程调用了某个对象的wait()方法后,JVM会把线程放入这个对象的等待池中,直到该对象的notify或notifyAll方法在其他线程中被调用,才有可能唤醒本线程。
2)位于对象锁池中:当线程处于运行状态,试图获取某个对象的monitor(锁)时,若该对象锁已经被其他线程占用,则JVM将当前线程放入该对象的锁池中,直到该对象的锁被释放,当前线程才有可能获得该对象锁并解除阻塞状态。
3)其他阻塞状态:当前线程执行sleep方法或者调用其他线程的join方法时。发出I/O请求时也会发生阻塞。

什么是同步?

线程同步:(协同步调)一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥:(共享资源访问排他性)当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥是线程同步到一种。

synchronized关键的问题

1)非静态synchronized方法

bubuko.com,布布扣
 1 public class ThreadTest {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         MyService sv = new MyService();
 5         ThreadDemo demo = new ThreadDemo(sv);
 6         Thread th1 = new Thread(demo);
 7         th1.start();
 8         
 9         Thread.sleep(500); //等待th1 进入运行状态
10         
11         sv.init();
12         th1.join();
13         System.out.println("main thread finish");
14     }
15 
16     static class ThreadDemo implements Runnable {
17         private Object obj;
18 
19         public ThreadDemo(Object obj) {
20             this.obj = obj;
21         }
22 
23         public void run() {
24             try {
25                 synchronized (obj) {
26                     Thread.sleep(10000);
27                 }
28             } catch (Exception e) {
29             }
30         }
31     }
32 
33     static class MyService {
34         synchronized void init() {
35             System.out.println("MyService init");
36         }
37 
38         synchronized void service() {
39             System.out.println("MyService service");
40         }
41 
42         void testState() {
43             System.out.println("MyService state");
44         }
45     }
46 }
View Code

运行结果是:mian线程等待10000毫秒后才调用sv.init()方法。因为th1里sv对象的monitor被一直持有10000毫秒。

结论:以synchronized修饰的非静态方法成为同步方法。线程需要获得该对象的monitor才能执行其同步方法。

2)静态synchronized方法 

bubuko.com,布布扣
 1 public class ThreadTest2 {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         ThreadDemo demo = new ThreadDemo();
 5         Thread th1 = new Thread(demo);
 6         th1.start();
 7         Thread.sleep(500); // 等待th1 进入运行状态
 8 
 9         MyService.testState();
10         System.out.println("main thread finish");
11     }
12 
13     static class ThreadDemo implements Runnable {
14 
15         public void run() {
16             try {
17                 MyService.testState();
18             } catch (Exception e) {
19             }
20         }
21     }
22 
23     static class MyService {
24         synchronized void init() throws InterruptedException {
25             System.out.println("MyService init");
26         }
27 
28         synchronized void service() {
29             System.out.println("MyService service");
30         }
31 
32         synchronized static void testState() throws InterruptedException {
33             System.out.println("MyService state");
34             Thread.sleep(10000);
35         }
36     }
37 }
View Code

wait()/notify()/notifyAll()

bubuko.com,布布扣
 1 public class Test {
 2     static Object lock = new Object();
 3 
 4     public static void main(String[] args) throws InterruptedException {
 5         Thread th = new Thread(new MyThread_1());
 6         th.start();
 7 
 8         Thread th2 = new Thread(new MyThread_2());
 9         th2.start();
10 
11         th.join();
12         th2.join();
13         System.out.println("main thread finish");
14     }
15 
16     static class MyThread_1 implements Runnable {
17 
18         public MyThread_1() {
19         }
20 
21         public void run() {
22             System.out.println("MyThread 1 run");
23             try {
24                 Thread.sleep(1000);
25                 synchronized (lock) {
26                     System.out.println("MyThread 1 obtain lock, and sleep");
27                     Thread.sleep(10000);
28                     System.out.println("MyThread 1 awake, and will release lock");
29                 }
30             } catch (Exception e) {
31             }
32             System.out.println("MyThread 1 finish");
33         }
34     }
35 
36     static class MyThread_2 implements Runnable {
37         public void run() {
38             System.out.println("MyThread 2 run");
39             try {
40                 synchronized (lock) {
41                     System.out.println("MyThread 2 obtain lock");
42                     lock.wait(3000);
43                     System.out.println("MyThread 2 re-obtain lock");
44                 }
45             } catch (Exception e) {
46             }
47             System.out.println("MyThread 2 finish");
48         }
49     }
50 }
View Code

 输出:

MyThread 1 run
MyThread 2 run
MyThread 2 obtain lock
MyThread 1 obtain lock, and sleep
MyThread 1 awake, and will release lock
MyThread 1 finish
MyThread 2 re-obtain lock
MyThread 2 finish
main thread finish

结论:当前线程执行lock.wait()必须拥有ock对象的monitor,而执行lock.wait()方法后,会释放monitor。lock.wait()执行完成后需要重新获取lock的monitor。
notify()/notifyAll()方法同样需要当前线程获得lock对象的monitor,会唤醒等待lock对象monitor的一个(所有)线程,但这(些)线程唤醒后必须重新获取lock的monitor

isAlive() 

当线程调用了start()方法后,isAlive()为true,直到线程死亡。

如何使一个线程让出CPU执行时间?

1)调整各个线程优先级
2)让线程执行sleep方法
3)让线程调用yield方法
4)让线程调用另一个线程的join方法。

线程优先级

setPriority(int)和getPriority()。如果想要保持可移植,应当设置:MAX_PRIORITY, MIN_PRIORITY, NORM_PRIORITY

线程睡眠

当线程运行时,执行sleep方法,会放弃CPU,进入阻塞状态。

线程让步

当线程在运行中执行了Thread类的yield()静态方法时,如果此时具有相同优先级的其它线程处于就绪状态,那么yield()方法将把当前运行的线程放到运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。

sleep与yield的区别

二者都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。区别如下:
1)sleep不考虑线程优先级,可能把CPU让给优先级较低的线程。yield只让给优先级相同或更高的线程执行的机会。
2)执行sleep后,线程进入阻塞状态;执行yield后,线程进入就绪状态。
3)sleep方法抛出InterruptedException;yield方法不抛出任何异常。
4)sleep方法比yield方法有更好的移植性。

join()

等待其它线程的结束。当前线程调用另外一个线程的join方法后,会转入阻塞状态,直到另一个线程运行结束才恢复到就绪状态。

Java的原子操作

原子操作:一个操作中的所有动作要么全做,要么全不做。原子操作是一个不可分割的基本单位,在执行过程中不允许被中断。
Java原子操作
    1.引用类型变量值的读和写。注意这儿是引用值的读写,而不是所引用对象内容的读和写。
    2.除了long和double之外的简单类型的读和写。由于他们是64bit,JVM基本存储单元是32bit,无法在这一个时钟周期操作完成。
    3.所有声明为volatile的变量的读和写,包括long和double类型以及引用类型对基本数据类型的赋值或者返回值操作。 
自增操作(++)不是原子操作,因为涉及一次读和一次写。

临界区:任意时刻只允许一个线程访问的公共资源或者代码块。
同步代码块:以synchronized标记的代码块。能够保证多个线程按照正常逻辑访问临界区。
同步锁每个JAVA对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。

线程同步的特征

1) 如果同步代码块与非同步代码块同时操作共享资源,同样会造成资源争用。
2)每个对象都有唯一的同步锁。
3)静态方法可以加synchronized修饰符,同步锁为该方法所属类的类对象。所有该类的synchronized static方法的同步锁为类的类对象。
4)进入同步块执行的线程并非不间断执行,sleep、yield方法会使得线程将CPU让给其他线程,但不释放锁。而调用同步锁对象的wait方法后同样会让出CPU,且会释放锁。
5)synchronized方法不被继承。子类若覆盖父类的synchronized 方法,则该方法不再是synchronized方法,除非声明为synchronized。

线程安全的类

1)该类的对象可以被多个线程安全访问。
2)每个线程能够正常执行原子操作,并得到正确结果。
3)每个线程的原子操作执行完成后,对象处于合理状态。

释放对象锁

1)线程执行完毕同步代码块(方法)。
2)线程在执行同步代码块过程遇到异常。
3)线程在执行同步代码块过程,调用锁所属对象的wait()方法,会释放锁,线程进入锁所属对象的等待池。

死锁

当一个线程A等待线程B持有的锁,而同时线程B等待线程A持有的锁(或者线程B间接等待线程A的锁),就发生了死锁

bubuko.com,布布扣
 1 public class DeathLock {
 2     public static void main(String[] args) throws InterruptedException {
 3         Object o1 = new Object();
 4         Object o2 = new Object();
 5         Thread th = new Thread(new MyThread(o1, o2));
 6         Thread th2 = new Thread(new MyThread(o2, o1));
 7 
 8         th.start();
 9         th2.start();
10     }
11 
12     static class MyThread implements Runnable {
13 
14         private Object lock;
15         private Object target;
16 
17         public MyThread(Object lock, Object target) {
18             this.lock = lock;
19             this.target = target;
20         }
21 
22         @Override
23         public void run() {
24             try {
25                 synchronized (lock) {
26                     Thread.sleep(1000);
27                     System.out.println(String.format("hold %s, wait for %s ",
28                             lock, target));
29                     synchronized (target) {
30                     }
31                 }
32             } catch (InterruptedException e) {
33                 e.printStackTrace();
34             }
35         }
36     }
37 
38 }
View Code

如何避免死锁?

基本原则:让所有线程按照同样顺序获得一组锁。 
方法:1)将多锁组成一个组放到一个锁下。
         2)标记各个锁对象锁的状态是否已被占用。这样当线程试图获取该某个对象的锁,而且锁已经被占用时,可以延迟进行再次尝试,避免死锁产生。
根本:仔细考虑涉及多线程的整个系统,对关键资源、模块进行深入分析。

线程通信

Java.lang.Object类中提供了两个用于线程通信的方法:

1)wait()方法。线程执行某个对象的wait方法后将释放该对象的锁,并进入到该对象的等待池中。等待其他线程将本线程唤醒。
2)notify/notifyAll方法。执行某个对象的这两个方法后,JVM从该对象的等待池中随机选择一个(全部选择)线程,并将它(们)放入该对象的锁池中。

 

 

多线程

标签:style   blog   http   color   os   使用   java   io   strong   

原文地址:http://www.cnblogs.com/xzzzzzzzz/p/3276732.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!