线程的组成:
任何线程都具有基本的组成部分:
- cpu时间片:
操作系统(OS)会为每个线程分配时间。
- 运行数据:
堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象。
栈空间:存储线程需要使用的局部变量,每个线程都有独立的栈。
- 线程的逻辑代码。
创建线程(1)
- 创建线程的第一种方式:
- import org.junit.Test;
public class test1 {
@Test
public static void main(String[] args) {
MyThread myThread=new MyThread(); //创建子类对象
myThread.start() ; //开启start方法
}
}
class MyThread extends Thread {//1继承Thread类
//覆盖run方法
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("MyThread:" + i);
}
}
}
- 创建线程(2)
创建线程的第二种方法:
public class test2 {
@Test
public static void main(String[] args) {
MyRunnable myRunnable=new MyRunnable();// 3 创建实现类对象
Thread thread=new Thread(myRunnable);// 4 创建线程对象
thread.start();//调用start方法
}
}
class MyRunnable implements Runnable {//1 实现Runnable接口
//2 覆盖run方法
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("MyRunnable:" + i);
}
}
}
线程的状态(基本)
常见方法:
休眠:
public static void sleep(long millis)
当前线程主动休眠milllis秒。
放弃:
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
结合:
public final void join()
允许其他线程加到当前线程中。
线程的状态(等待)
线程安全问题:
需求:
A线程将“Hello”存入数组的第一个空位,B线程将“World” 存入数组的第一个空位
线程不安全:
当多个线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
临界资源:
共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
原子操作:
不可分割的多步操作,被视作一个整体其顺序和步骤不可打乱或缺省。
线程安全:
同步方式(1)
同步代码块:
sychronized(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
注:每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程才能,才能进入该对象加锁的同步代码块。
线程退出同步代码块时,才会释放对应的互斥锁标记。
线程的状态(阻塞)
同步方式(2)
同步方法:
synchronized 返回值类型 方法名称(形参列表 0){//当前对象(this)加锁
//代码 原子操作
}
注:只有拥有对象互斥所标记的线程,才能进入该对象加锁的同步方法当中。
线程退出同步方法时,会释放相应的互斥锁标记。
同步规则:
注意:
只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
如果调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。、
已知JDK中线程安全的类:
StringBuffer
Vector
HashTable
以上类的公开方法,均为synchronized修饰的同步方法
经典问题:
1. 死锁:
-List item当一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
一个线程可使用拥有多个对象锁的标记,当线程阻塞时,不会释放
2.生产者,消费者
若干个生产者在生产这些产品,这些产品将提供给若干个消费者去消费,为了是消费者和生产者能够并发执行,两者之间设置一个能够存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,既不允许消费者从一个空的缓冲区中取走产品,也不允许生产者向一个满的缓冲区中放产品。
线程通讯
等待
- public final void wait() -
- public final void wait(long timeout)
- 必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时,此线程阻塞在o的等待队列中。释放锁,进入等待队列。
通知
- public final void notify()
- public final void notifyAll()
- 必须在对obj’加锁的同步代码块中。从obj的Waiting中释放一个或者释放全部线程。对自身没有任何影响。
小结
线程的创建
- 方法1:继承Thread类
- 方法2:实现Runnable接口(一个任务Task),传入Thread对象并执行
线程安全
- 同步代码块:为方法中的局部代码(原子操作)加锁
- 同步方法:为方法中的所有代码(原子操作)加锁
线程间的通信
- wait()/wait(long timeout):等待
- notify()/notifyAll():通知