java中关于线程的一些API

java中关于线程的一些API:

* 线程的启动

    线程的启动有两种方式:继承Thread类,复写run方法;实现Runnable接口,实现run方法。

    两种方式都可以,但切记继承Thread类时如果要启动线程需要调用的是start方法而不是run方法,如果调用的是run方法的话不会开启新线程,只会在当前线程中执行run方法中的代码,如下面的代码

public class ThreadStart {

public static void main(String[] args) {

System.out.println(Thread.currentThread().getName());

ThreadRun t = new ThreadRun();

t.run();

//t.start();

}

}

class ThreadRun extends Thread{

@Override

public void run(){

System.out.println(Thread.currentThread().getName());

}

}

上面的代码,如果调用的是run方法,得到的线程名字都是main线程,如果调用的是start方法得到的就是两个线程名字

* 线程中断

    关于线程中断有三种方式:

    public void interrupt//中断

    public static boolean interrupted()//判断是否中断并清除中断标志位

    public boolean isInterrupted//判断是否中断

关于中断和Thread中的stop方法是有区别的,调用stop方法是立刻停止线程,这样可能会造成数据不一致的现象,而调用interrupt方法是给当前线程添加中断标志,但不一定会立即中断

第一个是实例方法,它通知目标线程中断,也就是设置中断标志位。中断标志位表示当前线程已经被中断了;第三个也是实例方法,它判断目标线程是否有被中断;第二个是静态方法,也是用来判断是否有被中断,但它会清除目标线程的中断标志位。

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

在上述代码中,虽然对t进行了中断,但是线程并没有进行中断,如果想要中断需要进一步处理代码:

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

if(Thread.currentThread().isInterrupted()){

System.out.println("thread is interrupted");

break;

}

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

这样就会中断退出。如果在循环体中出现类似wait或者sleep等操作时,只能通过中断来识别

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

if(Thread.currentThread().isInterrupted()){

System.out.println("thread is interrupted");

break;

}

try {

Thread.sleep(2000);

}catch (InterruptedException e){

System.out.println("Interrupt when sleep");

//设置中断标志

Thread.currentThread().interrupt();

}

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

Thread.sleep()方法由于中断而抛出的异常,它会清除中断标志,如果不加处理,那么在下一次循环中就无法捕获到这个中断标志位。

* 等待(wait)和通知(notify)

    wait和notify必须使用在synchronized代码块中,当一个线程调用了wait方法时,这个线程就会进入等待状态并且会释放锁,当另外一个线程调用notify时,这个线程才会被唤醒(假设此处只有两个线程,并且等待队列中只有这一个线程)。

注意事项:wait和sleep方法都是让线程进行等待,最重要的区别是wait方法会释放当前锁,而sleep不会;线程调用notify方法之后并不会立刻释放锁,只有当notify之后的代码执行完毕之后才会释放锁。

public class ThreadWaitAndNotify {

private static Object object = new Object();

public static class T1 extends Thread{

@Override

public void run(){

synchronized (object){

System.out.println(System.currentTimeMillis()+" :T1 start!");

try {

System.out.println(System.currentTimeMillis()+" :T1 wait for object");

object.wait();

}catch (InterruptedException e){

e.printStackTrace();

}

System.out.println(System.currentTimeMillis()+" :T1 end!");

}

}

}

public static class T2 extends Thread{

@Override

public void run(){

synchronized (object){

System.out.println(System.currentTimeMillis()+" :T2 start!");

object.notify();

System.out.println(System.currentTimeMillis()+" :T2 end!");

try {

Thread.sleep(2000);

}catch (InterruptedException e){

}

}

}

}

public static void main(String[] args) {

Thread t1 = new T1();

Thread t2 = new T2();

t1.start();

t2.start();

}

}

运行结果如下:

1512288311968 :T1 start!

1512288311968 :T1 wait for object

1512288311969 :T2 start!

1512288311969 :T2 end!

1512288313970 :T1 end!

* 等待线程结束(join)和谦让(yield)

很多时候,一个的输入可能非常依赖另外一个或多个线程的输出,此时,这个线程就需要等待依赖线程执行完毕,才能继续执行。JDK提供了两个方法:

public final synchronized void join(long millis)//最大等待时间,当超过这个时间主线程就会继续执行

public final void join()//无限期等待,直到目标线程执行完毕

yield:

public static native void yield()

这是一个静态方法,一旦执行,它会使当前线程让出CPU,但要注意,让出CPU并不表示当前线程不执行了。当前线程在让出CPU后,还会进行CPU资源的争夺。

* 守护线程(daemon)

    当一个应用中只有守护线程时,表示这个java虚拟机就会自然退出

注意事项:设置守护线程时一定要设置在start方法之前,否则会报IllegalThreadStatException异常,报出这个异常并不会使线程停止,该线程还会继续执行。

* 线程优先级

    在Thread类中有表示线程优先级的字段

    public final static int MIN_PRIORITY = 1;

    public final static int NORM_PRIORITY = 5;

    public final static int MAX_PRIORITY = 10;

    数值越大,优先级越高。

* 线程安全(synchronized)

    并行程序的开发是以线执行结果的争取性为保证的,所以线程安全是并行程序开发额根本和根基。以下这个程序就是典型的线程不安全

public class ThreadSynchronizeDemo1 implements Runnable {

static ThreadSynchronizeDemo1 instance = new ThreadSynchronizeDemo1();

static volatile int i = 0;

public void increase(){

i++;

}

@Override

public void run(){

for(int j = 0; j < 1000000; j++){

increase();

}

}

public static void main(String[] args)throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(i);

}

}

上述的结果会小于2000000,线程t1和t2同时读取i为0,并各自计算得到i=1,并先后写入这个结果,因此,虽然i++被执行了2次,但是实际i的值只增加了1,想解决这个问题synchronized关键字是一个解决方案。

synchronized可以有多种用法:

    [x] 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁

    [x] 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁

    [x] 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁

这时对上述代码进行改造:

public synchronized void increase(){

i++;

}

在这个实例方法上加synchronized关键字进行同步

在上述代码尤其要特别注意:在创建线程时使用的是同一个对象实例,如果此时使用的两个不同的对象实例,在increase()方法上加synchronized关键字是不起作用的,如果想得到正确的结果而又是不同的对象实例,可以在increase()方法之前加上static关键字,让这个方法变为静态方法,这个synchronized关键字就会作用于这个类上。

* 并发下的list

    JDK中的util包中的list是线程不安全的类,如下面的代码:

public class ThreadList {

static List list = new ArrayList<>();

static class AddThread implements Runnable{

@Override

public void run(){

for(int i = 0; i <1000000; i++){

list.add(i);

}

}

}

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(new AddThread());

Thread t2 = new Thread(new AddThread());

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(list.size());

}

}

上面的代码可能会出现三种情况:

第一种:程序正常结束

第二种:抛出异常:Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 15  这是因为ArrayList在扩容过程中,内部一致性被破坏,但由于没有锁的保护,另外一个线程访问到了不一致的内部状态,导致出现越界问题。

第三种:程序正常结束,但是结果不正确。

你可能感兴趣的:(java中关于线程的一些API)