线程的创建方式
- 声明一个Thread类的子类,并覆盖run()方法,并创建该类对象
Class MyThread extends Thread(){
public void run(){
//覆盖该方法
}
}
Thread thread = new MyThread (); - 以Runnable接口实现类对象为构造参数创建Thread类对象
Class MyRunnable implements Runnable(){
public void run(){
//实现该方法
}
}
Thread thread = new Thread(new MyRunnable());
线程的启动
注:要启动一个线程,必须调用线程的线程的start()。
调用start()方法时,该线程启动,并被放入线程调度队列中。JVM负责调度该线程,并调用该线程的run()方法。
run()方法是具体实现线程的功能,不能通过run()方法来启动线程。
线程的状态
- 新建(new):
线程被创建,但start方法没有调用,处于新建状态 - 就绪/可运行(RUNNABLE):
线程的start方法已经被调用,正在等待CPU的使用权,此时即为就绪状态 - 运行(RUNNING):
线程占用CPU,执行程序代码,此时线程即为运行状态 - 阻塞(BLOCKED/ WAITING/ / TIMED_WAITING):
线程对象放弃CPU,暂时停止运行,不会在竞争占用CPU,此时线程即处于阻塞状态。
表现形式:
睡眠(Sleeping):线程通过执行sleep()方法暂时终止;
等待(WAITING/ / TIMED_WAITING):如果调用wait(),join()方法,线程将处于等待状态,用于协调两个或者多个线程并发运行
等待I/O操作等等…
处于阻塞状态下的线程对象不会竞争和占用CPU,
结束阻塞状态,线程进入就绪状态,而非运行状态 - 死亡( D TERMINATED):
线程对象的run()方法执行完毕(或者是stop方法被调用之后),线程就处于死亡状态
部分方法
- sleep():使当前线程睡眠,从运行进入阻塞状态
- 调用其他线程的join方法,使当前线程等待其他线程运行完毕
- 线程由于等待一个I/O事件被阻塞
- 对象的wait()方法,使当前线程等待(需和同步机制配合使用)
- 对象的yield()方法,使当前线程显示让出CPU控制权(让步)。从运行状态进入就绪状态
线程调度
概念:合理分配多个线程对CPU资源的占用,即线程调度
线程调度的原因:多线程对CPU大量需求与CPU的数量不足之间的矛盾
线程调度的策略:
1. 任何线程都有占用CPU的机会
2. 防止线程长期占用CPU不放弃,即应该控制线程适当放弃对CPU的占用
3. 符合其他策略目标
线程调度的实现手段:
合理设定优先级
使用Thread.yield()、Thread.sleep()、join()等方法
守护线程
其他非守护线程运行结束后会自动结束的线程
相关方法:
final void setDaemon(boolean on):将线程标记为守护线程
final boolean isDaemon();检查线程是否是守护线程
多线程
线程同步
基本概念:
线程同步是指在访问共享资源时,多个线程相互之间的协调和控制
线程同步的目的:实现多线程对共享资源有序可控访问,保障共享资源数据安全,避免死锁使整个系统正常运行
实现线程同步的方式是:互斥和协作
注:
并行程序设计中,共享资源越多,程序设计将越复杂,所以应该尽量减少共享资源的使用
线程的互斥同步
当有多个并行线程执行时,由于线程占用和放弃CPU在微观上无法预知,所以对于共享数据的插入、删除、更新等操作,若不采取一定措施,所取得数据很有可能不正确。
为了避免上述状况,java提供了一种同步机制的办法,即互斥。使用Synchronized关键字即加锁的方式来实现。
互斥:指临界资源同时只允许一个线程对其进行访问,具有唯一性和排他性。保证了线程对临界资源访问的原子性。
Synchronized关键字:保证同时只有一个线程执行特定的代码块,即实现该代码块的原子性。
三种方式:
同步代码块、同步实例方法、同步类方法
同步代码块:
synchronized (obj){
//...
}
obj是一个对象,可以称为该代码块的锁对象
同步实例方法:
public synchronized void show(){//功能代码}
同步静态方法:
public static synchronized void show(){//功能代码}
死锁
当两个线程循环依赖于一对同步对象时,发生死锁。
Eg:一个线程进入对象obj1的监视器,并且等待对象obj2的监视器。但另一个线程进入obj2监视器,并且等待对象obj1监视器。
死锁很少发生,一旦发生就很难调试。
线程协作
线程协作是多线程同步互斥的基础上,使线程之间依照一定条件,有目的、有计划的交互协同工作。这是一种比较高级的线程同步方式。
Java提供了一个精心设计的线程间通信机制,即wait-notify机制,通过该机制可以实现线程协作。
wait-notify机制是通过使用wait(),notify()和notifyAll()三个方法来实现的。(这三个方法都是Object类中,final修饰的实例方法。这三个方法必须在synchronized代码中应用,而且只有锁对象才可以调用。即持有锁对象监听器的线程才可以调用锁对象的这三个方法)
wait():调用该方法的线程退出监听器并进入等待状态,直到其他线程进入相同的监听器并调用notify()方法;
notify():通知等待(该方法所属对象)监听器的(多个)线程中的一个结束等待
notifyAll():通知等待(该方法所属对象)监听器的所有结束等待(即唤醒所有等待当前线程所持有的监听器的线程)
线程协作实例:
//橱柜
public class Cupboard{
private int count;
private int size;
public Cupboard(int size){
this.size = size;
}
public void add (){
count++;
syso(Thread,currentThread().getName()+":放入一个面包,橱柜内的面包数为"+count);
}
public void remove(){
count--;
syso(Thread,currentThread().getName()+":取出一个面包,橱柜内的面包数为"+count)
}
public boolean isEmpty(){
return count<1;
}
public boolean isFull(){
return count>=size;
}
}
//面包师
public class Baker extends Thread(){
private Cupborad cupborad;
public Baker(Cupborad cupborad){
this.cupborad = cupborad;
this.setName("面包师");
}
public void run(){
try{
for(int i = 0;i<100;i++){
synchronized(cupborad){
while(cupborad.isFull()){
syso(this.getName+"橱柜满,等待")
cupborad.wait();
}
cupborad.add();
cupborad.notifyAll();
Thread.sleep(1000);
}
}catch(e){
}
}
}
}
//伙计
public class Salesman extends Thread{
private Cupborad cupborad;
public Salesman(Cupborad cupborad){
this.cupborad = cupborad;
this.setName("伙计");
}
public void run(){
try{
for(int i = 0;i<100;i++){
synchronized(cupborad){
while(cupborad.isFull()){
syso(this.getName+"橱柜空,等待")
cupborad.wait();
}
cupborad.remove();
cupborad.notifyAll();
Thread.sleep(1000);
}
}catch(e){
}
}
}
}
//测试
public class store{
public static void main(String[] args){
Cupborad cupborad = new Cupborad(5);
Baker baker = new Baker(cupborad);
Salesman salesman = new Salesman(cupborad);
baker.start();
salesman.start();
}
}
同一进程中线程共享的资源
1. 堆 (由于堆是在进程空间中开辟出来的,因此new出来的都是共享的)
2. 全局变量 (与某一函数是无关的,因此也与某一线程无关)
3. 静态变量 (从属于类)
4. 文件等公共资源
多线程使用共享资源
- 方法一:
public class A {
private int i;
public A(int i) {
this.i = i;
}
public synchronized void add() {
++i;
System.out.println(Thread.currentThread().getName()+"********"+i);
}
public synchronized void del() {
--i;
System.out.println(Thread.currentThread().getName()+"********"+i);
}
public synchronized int getI() {
return this.i;
}
}
public class TestThread {
public static void main(String[] args) {
A a = new A(10);
new Thread(new Runnable() {
@Override
public void run() {
a.add();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
a.del();
}
}).start();
System.out.println(a.getI());
}
}
- 方法二:
public class A {
private int i;
public A(int i) {
this.i = i;
}
public synchronized void add() {
++i;
System.out.println(Thread.currentThread().getName()+"********"+i);
}
public synchronized void del() {
--i;
System.out.println(Thread.currentThread().getName()+"********"+i);
}
public synchronized int getI() {
return this.i;
}
}
public class Th implements Runnable{
private final A a;
public Th(A a) {
this.a = a;
}
@Override
public void run() {
a.add();
}
}
public class Th2 implements Runnable{
private final A a;
public Th2(A a) {
this.a = a;
}
@Override
public void run() {
a.del();
}
}
public class Test {
public static void main(String[] args) {
A a = new A(0);
Thread del = new Thread(new Th2(a));
del.start();
Thread add = new Thread(new Th(a));
add.start();
}
}