1.线程的创建
(1)继承Thread类 ,重写run方法
线程启动时,调用start(),不能调用run()。
(2):声明实现 Runnable
接口的类。该类然后实现 run
方法。然后可以分配该类的实例,
在创建 Thread
时作为一个参数来传递并启 动。
class MyRun implements Runnable {
...
public void run() {
. . .
}
}
然后,下列代码会创建并启动一个线程:
Runnable p = new MyRun(); Thread t=new Thread(p); t.start();
(3):上述两种情况都可以使用匿名内部类完成线程的创建
2.main方法,是jvm调用的执行程序的入口,也是一个线程。
同步锁:
理解:对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它了。
锁语句产生了一种互相排斥的效果
关于同步锁:以下几点介绍
1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
2、当一个线程访问object的一个synchronized(this) 同步代码块时,其他线程对object中所有其它synchronized(this) 同步代码块的访问将被阻塞。
3、当一个线程访问object的一个synchronized(this) 同步代码块时,它就获得了这个object 的对象锁。结果,其它线程对该object 对象所有同步代码部分的访问都被暂时阻塞
什么时候该使用同步锁-synchronized??
如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一 个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。
如何使用同步锁:
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
- synchronized 方法:
在方法的修饰词加 synchronized
例:public static synchronized void show (int a){ } - synchronized 块:
synchronized (this){ //this:表示当前这个类
//控制访问的资源
} - 注意:每个对象只有一个锁,在哪个对象上锁很关键;
只能同步方法,不能同步变量和类。 - 记住:①线程同步就是线程排队,线程同步的目的就是避免线程“同步”执行;
②“共享”,只有共享资源的读写访问才需要同步,如不是共享资源,就不需要同步。 - 难点:wait()/notify():属于对象,不属于线程;
wait():表示等待,让出CPU,把锁让出,处于休眠状态,等待被唤醒;
notify()/notifyAll():通知其他线程对象,唤醒休眠的对象,没有因notify()而中断线程。 - join():起阻塞作用,A,B两个线程,A执行到B.join()后,A就会等待,等B执行完,A才会执行。
- yield():暂停当前线程,执行其他线程让出CPU,给其他线程执行的机会;
3.线程的常用API:
- getName(): 返回该线程的名称。
- getId():返回线程的标识符;
- getState():返回线程的状态;
- interrupt():中断线程;
- currentThread():static, 返回对当前正在执行的线程对象的引用。获取当前所处线程;
- join():等待该线程终止;
- join(long mills):等待该线程终止的时间最长毫秒数;
- start():使该线程开始执行,Java虚拟机调用该线程run()方法;
- sleep(long time):静态方法。
使当前线程进入阻塞状态多少毫秒(让出时间片段)
超出time时间,自动进入runnable状态。
在time时间内,有可能被打断,会出现打断异常。 - wait()/notify():作用是协调多线程的工作。
- wait()/notify()/notifyAll():
作用:使多线程进行协调工作。锁对象作为协调者。
方法都是锁对象的。
作用:使多线程进行协调工作。锁对象作为协调者。
方法都是锁对象的。
obj.wait():告诉当前线程需要等待。
obj.notify():当前线程通过锁对象,通知等待的线程
obj.notifyAll():通过锁对象,通知所有等待的线程。 - interrupt():打断当前阻塞状态下的线程。(谁调用,谁就会被打断)
线程只有处在睡眠状态时,才会被打断。 - 当多个线程同时请求synchronized方法或块时,monitor(锁)会设置几个虚拟逻辑数据结构来管理这些多线程。下图是简化了的管理结构。
线程安全问题:
当多个线程同时访问某一处资源时,会发生线程安全问题。
这类资源可能是:
(1)静态资源
(2)实例属性
怎么处理这种现象呢?我们应该让多线程异步访问这个资源
变成同步访问这个资源。
异步:多个线程"同时"进行,各干各的。(爬卡车)
同步:多个线程要按照顺序,一个访问完,下一个才能访问.(上公交)
解决办法:--同步锁机制
1)关键字synchronized(同步),java语言可以为某一块代码提供内置锁。
格式:synchronized(锁对象){
// 。。。
}
也可以修饰方法:将此方法上锁。
此时: 锁对象:
静态方法:类名.class。
动态方法:对象。
解释:当某一线程执行到此代码时,会获取一个内置锁,将此代码块从
里面上锁。其他线程碰到此代码块时,需要等待正在运行的线程
运行完这个代码块后(会释放内置锁),才可以获取内置锁对象。
2) 锁:java的任何对象都可以作为锁对象。但是必须保证多个线程使用的是
同一个锁。如果锁不是同一个,那么解决不了线程安全问题。
可以对方法进行直接加锁机制,有锁,锁是类对象:获取类对象的方法:
类名.class,类对象是唯一的。
3)另外:在使用锁机制时,要注意效率。锁入的代码块越少效率越高。
静态和动态方法:
4)在使用锁机制时,不互斥(不会影响)。
1)静态方法的锁对象是类对象。
2)动态方法的锁是对象。
5)要求:对锁对象的要求:多个线程使用的是同一个锁对象。
线程池:
Java语言为我们提供了一个管理线程的类型ExecutorService
概念:创建多个线程,集合到一起就是线程池
作用:自动创建线程,重用线程。
当创建大量的线程时,在线程结束就会有内存开销,线程的频繁切换也会造成内存开销。
很容易造成系统崩溃。
线程池对象为我们提供了固定数量的线程。 如果由多个任务,可以
交给线程池。由线程池来分配任务,交给空闲的线程来执行任务,
当任务执行完毕,线程不结束,回到空闲状态。