Bootstrap

线程与进程(二)

1 Thread类中常用的一些方法:

方法名  功能说明  
start() 启动一个新线程,在新的线程运行run方法中的代码 start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
run()新线程启动后会调用的方法 如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建 Thread 的子类对象,来覆盖默认行为
join()等待线程运行结束  
join(long millis) 等待线程运行结束,最多等待 millis毫秒 
join(long millis,int nanos)等待线程运行结束,最多等待 millis毫秒,nanos纳秒    
getId()  获取线程长整型的 id id 唯一
getName()  获取线程名
setName(String name) 修改线程名    
getPriority()获取线程优先级  
setPriority(int newPriority)  修改线程优先级   java中规定线程优先级是1~10 的整数,较大的优先级能提高该线程被 CPU 调度的机率
static yield() 提示线程调度器让出当前线程对CPU的使用    主要为了调试和测试
static sleep(long millis) 使得当前线程阻塞millis毫秒
static sleep(long millis, int nanos)使得当前线程阻塞millis毫秒加上nanos纳秒 
static   currentThread() 获取当前正在执行的线程 
isAlive() 判断线程存活状态  
interrupt()  打断线程如果被打断线程正在 sleep,wait,join 会导致被打断的线程抛出InterruptedException,并清除 打断标记 ;如果打断的正在运行的线程,则会设置 打断标记 ;park 的线程被打断,也会设置 打断标记
isInterrupted() 判断是否被打断 不会清除打断标记
static interrupted() 判断当前线程是否被打断会清除打断标记
getState()获取线程状态 Java 中线程状态是用 6 个 enum 表示,分别为:
NEW, RUNNABLE, BLOCKED, WAITING,
TIMED_WAITING, TERMINATED

1.1 线程休眠方法-static void sleep

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            try {
                //秒杀---
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~"+i);
        }
    }
}

1.2 yield 当前线程让出cpu-参与下次的竞争

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~"+i);
            Thread.yield();//当前线程让出cpu参与下次的竞争。
            System.out.println("~~~~~~~~~~~~~~~~");
        }
    }
}

1.3 join加入当前线程上

插入的线程执行完毕后,当前线程才会执行。  

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1=new MyThread();
        t1.setName("线程A");
        MyThread2 t2=new MyThread2();
        t2.setName("线程B");


        t1.start();
        t2.start();

        t1.join(); //t1加入主线程上,主线程需要等t1执行完毕后才会执行. 如果主线程需要等带t1和t2线程的执行结果 做下一步操作时。
        for (int i = 0; i <20 ; i++) {
            Thread.sleep(10);
            System.out.println("main~~~~~~~~~~~~~~~~~~~~~"+i);
        }
    }
}

1.4 setDaemon()设置线程为守护线程

当所有线程执行完毕后,守护线程也会终止。

//JDK--默认就有一个守护线程.GC垃圾回收。
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1=new MyThread();
        t1.setName("线程A");
        t1.setDaemon(true);//设置t1为守护线程
        t1.start();
        for (int i = 0; i <20 ; i++) {
            System.out.println("main~~~~~~~~~~~~~~~~~~~~~"+i);
        }
    }
}

守护线程(Daemon Thread): 在程序运行时 在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。任何一个守护线程都是整个JVM中所有非守护线程的“保姆”。

守护线程是一种在后台提供服务的线程,它的存在并不会阻止Java虚拟机(JVM)的退出。

当所有的非守护线程执行完毕或者终止时,JVM会检查是否还有任何活动的守护线程。如果只剩下守护线程,JVM会优雅地关闭守护线程并退出。

可以通过调用Thread类的setDaemon(true)方法将线程设置为守护线程。

用户线程(非守护线程,Non-daemon Thread): 用户线程基本上和守护线程一样,唯一的不同之处在于如果用户线程全部退出运行,只剩下守护线程存在了,JVM就会退出。因为当所有非守护线程结束时,没有了被守护者,守护线程也就没有工作可做,当然也就没有继续执行的必要了,程序就会终止,同时会杀死所有的"守护线程",也就是说只要有任何非守护线程还在运行,程序就不会终止

非守护线程是一种正常的线程,它的存在会阻止JVM的退出,直到所有的非守护线程执行完毕或者手动调用System.exit()方法退出程序。
默认情况下,通过Thread类创建的线程都是非守护线程。

 2 解决线程安全问题

2.1 什么情况下会出现线程安全问题

当多个线程操作同一个资源时,则出现线程安全问题。

2.2 java如何解决线程安全问题

提供了两种方式:第一种:使用synchronized自动锁

第二种: 使用Lock手动锁。使用锁相对于把原来的异步转换为同步操作。

 使用synchronized关键字解决

它可以使用在方法上,也可以使用在代码块中。

synchronized(共享锁对象){
 同步代码块。

}

 

public class SellTicket implements Runnable {
    private int tick = 100;
    private static Object o = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (tick > 0) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    tick--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
                } else {
                    break;
                }
            }
        }
    }
}
 public synchronized void sell(){
        if (tick > 0) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            tick--;
            System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
        }
    }

使用Lock手动

Lock它是一个接口,它的实现类。ReentrantLock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
    private int tick = 100;

    private Lock l = new ReentrantLock();

    //synchronized使用在方法那么它的共享锁为this
    @Override
    public void run() {
        while (true) {
            try {
                l.lock();//加锁
                if (tick > 0) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    tick--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
                } else {
                    break;
                }
            } finally {
                l.unlock();//解锁
            }

        }
    }


}

 

syn和lock区别

syn可以使用代码块和方法。自动加锁和释放锁。不会出现死锁问题。

lock它只能使用在代码块中。需要手动加锁和释放锁。如果不释放锁,死锁问题。灵活。它的释放锁必须放在finally.

3 死锁

线程A拥有锁资源a,希望获取锁资源b,线程B拥有锁资源b,希望获取锁资源a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。

解决:

1. 不要使用锁嵌套。
2. 设置超时时间。--Lock类中tryLock.
3. 使用安全java.util.concurrent下的类。 

4 线程通信

wait方法和notify方法。

wait 和 notify 方法不是线程对象的方法,是java中任何一个java对象都拥有的方法,因为这个是Object类中自带的方法。
wait() 和 notify() 方法的作用:
Object o = new Object();

o.wait(): 让正在o对象上活动的线程进入无期限等待状态,直到被唤醒为止。
o.notify(): 唤醒正在o对象上等待的线程。
—— notifyAll()方法:这个方法是唤醒o对象上处于等待的所有线程。

5. 线程状态

   NEW,====新建状态。
        RUNNABLE,===>就绪状态和运行状态
        BLOCKED,===>堵塞状态
        WAITING,====>等待状态
        TIMED_WAITING,===>时间等待
        TERMINATED;===终止。
通过调用不同的方法相互转换

 

 

;