文章目录
  1. 1. 运行java程序两个线程
  2. 2. 多线程的创建方法
  3. 3. 线程的生命周期
  4. 4. 线程安全
    1. 4.1. 解决思路
      1. 4.1.1. 同步函数
    2. 4.2. 单例下的多线程安全
    3. 4.3. 死锁
  5. 5. 多线程通信
    1. 5.1. 等待唤醒机制
  6. 6. java.util.concurrency.locks.*
  7. 7. wait 和 sleep 的区别
  8. 8. 停止线程的方法
  9. 9. 守护线程
  10. 10. join
  11. 11. 线程其他设置

运行java程序两个线程

  1. 执行main函数的线程 该线程的任务代码都定义在main函数中

  2. 负责垃圾回收的线程, 调用 System.gc()

多线程的创建方法

  1. 继承Thread

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    class MyThread extends Thread {
        public void run(){
           // Do some thing
           /* 获取线程的名称 Thread-编号
           * 在创建的同时已经给予编号
           */
           getName();
           Thread.currentThread().getName(); //当前运行线程的名称
        }
    }
    Thread t = new MyThread();
    t.start(); //开启线程
    
  2. 实现runable接口

    好处: 1. 将线程任务从线程的子类中分离出来, 进行单独封装

    1. 避免单继承的局限性
      1
      2
      3
      4
      5
      6
      
      class Demo implements Runnable{
         public void run(){}
      }
      Thread t = new Thread(new Demo());
      t.start();
      t.start(); // 多次启动会报异常
      

线程的生命周期

  1. 被创建 new()
  2. 运行 start()
  3. 消亡 stop() 或 等任务结束
  4. 冻结 sleep()wait()

    wait()不占有锁, 使用notify()唤醒; sleep()占有锁, 自己唤醒.

线程安全

产生的原因:

  1. 多个线程在操作共享的数据
  2. 操作共享数据的线程代码有多条

当一个线程在执行操作共享数据的多条代码的过程中, 其他线程参与了运算, 就会产生线程安全问题.

1
public void run(){if(num>0) num++;}

解决思路

必须在当前代码执行完以后, 其他线程才能参与运算

1
2
3
synchronized (this) {
    if(num>0) num++;
}

弊端: 降低了效率

前提: 多个线程在同步中必须使用同一个锁

1
2
3
4
private obj = new Object();
synchronized(obj){
    if(num>0) num++;
}

同步函数

1
2
3
public synchronized void run(){
    if(num>0) num++;
}

同步函数和和没有指定this对象同步锁, 锁定的是同一个对象this

1
public static synchronized void run(){} // 锁定的对象是this.getClass()

单例下的多线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Single{
    private static Single s = null;
    private Single(){}
    public static Single getInstance(){
        if(s==null) s = new Single();    // 线程安全
        return s;
    }
    // 解决方方法一: 写成同步函数
    // 解决方法二:
    public static Single getInstance(){
        if(s==null){  // 解决效率问题
            synchronized(Single.class){  // 解决线程安全问题
                if(s==null) s = new Single()
            }
        }
        return s;
    }
}

死锁

1
2
3
4
5
6
7
8
9
public synchronized void run(){
    synchronized(obj){
    }
}
public void run(){
    synchronized(obj){
       synchronized(this){}
    }
}

多线程通信

多个线程在处理同一资源, 但是任务却不同.

等待唤醒机制

  1. wait(): 让线程处于冻结状态, 被wait的线程会存储在线程池中.
  2. notify(): 唤醒线程池中的一个线程.
  3. notifyAll(): 唤醒线程池中的所有线程.

这些方法必须定义在同步中,
因为这些方法是用于操作线程状态的方法.
必须要明确到底操作的是哪个锁上的线程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Thread1:
synchronized(r){
    while(flag) r.wait(); //不能是if, 会出现数据错误
    doSome();
    flag = true;
    r.notifyAll();  // 如果是用notify, 可能会阻塞
}

//Thread2:
synchronized(r){
    while(!flag) r.wait();
    doSome();
    flag = false;
    r.notifyAll(); 
}
1
2
3
4
5
//都必须捕捉
try{
    wait();
    sleep();
}catch(InterruptedException e) {}

java.util.concurrency.locks.*

jdk1.5以后将同步和锁封装成了对象

Lock 替代了 synchronized 方法和语句, 可以加上多组监视器.

Condition 替代了 notify()wait()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Lock是接口
Lock lock = new ReentrantLock(); // 互斥锁

lock.lock();
try{
  doSome();
} finally{
    lock.unlock();
}

Condition cond = lock.newCondition();
cond.await();
cond.singnal();
cond.singnalAll();

From API Referrence:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   }
 }

wait 和 sleep 的区别

  1. wait可以指定时间也可以不指定, sleep必须指定时间

  2. 在同步中时, 对cpu的执行权和锁的处理不同.

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

停止线程的方法

  1. 调用 stop() susppend()方法, 已经过时, 由安全问题
  2. run() 方法结束

    1
    2
    3
    4
    
    // 控制, 但是线程处于冻结状态, 无法读取标志
    public void run(){
        while(flag){ doSome(); }
    }
    
  3. 调用 interrupt(),让线程从冻结状态中强制恢复过来,sleep()wait() 会抛出异常

守护线程

thread.setDeamon(true) 必须在启动线程钱调用, 当正在运行的 的线程都是守护线程时, java虚拟机退出.

join

thread.join() 主线程等待thread线程终止, 再执行.

线程其他设置

1
2
3
4
5
6
7
8
9
/**设置线程优先级**/
thread.setPriority(THREAD.MAX_PRIORITY); // 最大为10

/**设置线程组**/
new Thread(TreadGroup tg)
tg.interrupt();

/* yield */
thread.yield(); //暂时释放执行权
文章目录
  1. 1. 运行java程序两个线程
  2. 2. 多线程的创建方法
  3. 3. 线程的生命周期
  4. 4. 线程安全
    1. 4.1. 解决思路
      1. 4.1.1. 同步函数
    2. 4.2. 单例下的多线程安全
    3. 4.3. 死锁
  5. 5. 多线程通信
    1. 5.1. 等待唤醒机制
  6. 6. java.util.concurrency.locks.*
  7. 7. wait 和 sleep 的区别
  8. 8. 停止线程的方法
  9. 9. 守护线程
  10. 10. join
  11. 11. 线程其他设置