天天看点

Java复习笔记之线程问题

一、线程的基本问题

操作系统中可以独立运行的程序叫进程,进程有多个执行单元,这些执行单元可以叫做线程。

一个进程至少包括一个线程,线程也叫轻量级进程。

同操作系统调用进程一样,进程里的多个线程看似同时执行。其实也是由CPU轮流执行的,只不过CPU运行速度很快,故而给人同时运行的错觉。

建立线程有两种方式

1.继承Thread类 

class MyThread extends Thread{

     public void run(){……};//一定要写的方法,必须这么写

//必须这么写的原因有三条

//1.如果改变public为protected等,然而父类中的run方法还是public。规则是子类不能比父类的级别低,更改的话就违反了这条

//2.如果添加参数,则变为普通方法,不会在调用start()方法后执行run()方法。

//3.只认void的返回值,否则编译时就会报错

}
           

创建线程对象时则使用 MyThread my = new MyThread();

my.start();//start()方法用于启动新线程,系统会自动调用run()方法。

若要为进程重命名则可以重写MyThread的构造方法,比如

public MyThread(String string) {
        this.setName(string);
    }
           

调用时使用new MyThread("name1")就行了。

2.实现Runnable接口

class MyThread implements Runnable{

 public void run(){……};//Runnable中只有这一个方法,所以一定要实现

}

创建线程对象时,需要调用Thread()的构造函数才行

MyThread my = new MyThread();

Thread td = new Thread(my);

td.start();
           

简化版1

Thread td = new Thread(new MyThread());

td.start()
           

;

简化版2

new Thread(new MyTherad()).start();

Thread的构造方法中还可以传入线程名比如new Thread(my,“线程名”)

两种方法的对比。

Runnable 更适合多个线程处理一个资源的情况,把线程同程序代码、数据有效的分离,很好的体现了面向对象的设计思想。

Thread创建的不同线程拥有不同的资源。

Runnable 可以避免Java的单继承带来的局限性。

获取当前线程的名字可以通过Thread.currentThread().getName();

二、线程的同步问题

实现同步问题有两个方法,同步代码块和同步方法,关键字是synchronized

容易知道,同步代码块的锁是用户自定义的任意对象。

那么同步方法当然也有锁,就是调用该方法的对象,也就是this指向的对象,可以用this获取。因为对象对线程来说也具有唯一性,所以可以作为锁。

如果同步的是静态方法,不需要创建对象就可以调用,那么静态方法的锁就是该方法所在类的class对象,可以用“类名.class”获取。

同步解决了多个线程同时访问共享数据的线程安全问题,即同一时间只能由一个线程访问公共资源。

但是因为每次都要判断锁的状态,所以非常消耗资源,效率低。

当两个线程都需要使用对方的锁,但又不释放自己的锁就会造成死锁。

java虚拟机没有检测,也没有采用措施来处理死锁情况,所以多线程编程是应该采取措施避免死锁的出现。

一旦出现死锁,整个程序即不会发生任何异常,也不会给出任何提示,只是所有线程都处于堵塞状态。

三、其他注意的

1.解决线程之间的通信问题的方法wait()、notify()、notifyAll()。

2.sleep()和wait()方法的区别?

sleep()方法让出CPU但不会释放同步锁,等时间到了之后再执行当前线程

wait()在同步锁内调用,释放同步锁,被唤醒后进入就绪状态等待下一次获得同步锁

3.yield()和join()

yield()让出控制权,进入就绪状态使系统重新调用一次,不会阻塞进程。某个进程yield()之后,只有优先级>=当前进程优先级的才有可能被调用

当A线程中使用B.join()时,A将被阻塞,等B执行完时A才能继续执行。

4.不同操作系统对线程优先级的支持不一样,不能很好的和Java中的线程优先级一一对应,所以不能依赖优先级。

5.对Java来说,只要还有一个前台线程在运行,这个进程就不会结束,但只有后台线程在运行,这个进程就会结束。

可以使用   线程.setDaemon(true),来使前台线程转换成后台线程,而且必须在start()方法之前使用,否则报错。

前后台线程是相对的概念,新创建的线程默认都是前台线程。

6.线程的5种状态和转换

.

Java复习笔记之线程问题