多线程
进程:一个应用程序或软件
线程:一个进程的执行场景或执行单元
一个进程可以启动多个线程
注意:
- 进程A和进程B的内存独立不共享
- 在Java中:线程A和线程B,堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈,每个栈之间互不干扰,各自执行,这就是多线程并发。
创建进程方法:
- 创建一个类继承Thread重写run()方法
public class Thread {
public static void main(String[] args) {
// TODO 自动生成的方法存根
MyThread mt=new MyThread();
mt.start();//开启一个线程,立马结束,这并不是并发
}
}
class MyThread extends Thread {//编写一个类直接继承Thread
public void run() {//重写run方法
//这段程序运行在分支线程中
for(int i=0;i<100;i++) {
System.out.println("分支线程->"+i);
}
}
}
- 实现Runnable接口
public class Thread {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Thread t=new Thread(new MyThread());
t.start();//开启一个线程,立马结束,这并不是并发
}
}
class MyThread implements Runnable {
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i=0;i<10;i++) {
System.out.println("分支线程->"+i);
}
}
}
3.实现Callable接口,可以获取线程的返回值,前面的是无法获取的,但是效率比较低。
public static void main(String[] args) {
// TODO 自动生成的方法存根
FutureTask ft=new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
// TODO 自动生成的方法存根
System.out.println("123");
return 1;
}
});
Thread t=new Thread(ft);
t.start();
Object a=null;
try {
a = ft.get();//获取返回值
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (ExecutionException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(a);
}
线程方法
- start():启动一个分支线程,只为开启一个线程空间,开启空间后会立马结束,启动成功的线程会自动调用run方法
- setName(String ):获取线程名字
- String getName():获取线程名字
- static Thread currentThread():获取当前线程,这段代码出现在哪里,线程就是那个,这个方法是静态方法
- static sleep(long millis):让当前线程进入休眠,进入阻塞状态,放弃cpu时间片,让给其他线程使用,参数是毫秒,需要异常处理
- intrrupt():叫醒线程,用的是异常处理机制
- stop():终止线程,已过时
- int getpriority():获取线程优先级(1~10)最低是1,默认是5
- void setPriority(int ):设置优先级
- static void yeild():让位方法,暂停当前正在执行的线程,并执行其他线程,它不是阻塞,会让当前线程运行状态变成就绪状态。回到就绪还可能再抢到
- void join():合并线程
t.join()//当前线程阻塞,t线程执行,t线程结束,才运行。
- setDaemon(boolean ):设置守护线程。
线程生命周期
调用start方法会出现就绪状态,run方法的开始执行标志着进入运行状态,当之前占有的时间片用完过后,会重新回到就绪状态继续抢夺时间片,抢夺到时间片后,会重新进入run方法接着上次代码继续执行下去。run方法执行结束标志进入死亡状态。
新建状态:刚new出来的对象
就绪状态:也叫可以运行状态,表示当前线程可以抢夺cup时间片的权力,当抢夺到一个cpu时间片后,开始执行run方法。
运行状态:run方法执行
阻塞状态:当一个线程出现阻塞事件,例如接收用户键盘输入、sleep方法,此时线程会进入阻塞状态,阻塞状态的线程会放弃之前占有的时间片。是由运行状态转变过来的,阻塞状态结束会回到就绪状态。
死亡状态:run方法执行结束
线程的调度
抢占式:那个线程的优先级比较高哪个就抢到的概率高一些(Java使用)
均分式:平均分配cpu时间片
线程安全
线程同步:线程排队执行,不能并发,会牺牲效率,用于解决线程安全问题。
同步编程:线程t1在执行之前必须等待t2执行结束,两线程之间发生了排队关系0
异步编程:线程t1和线程t2,各自执行各的,互不相干
synchronized
线程同步代码块
将要是共享的代码放入即可
synchronized(){//括号里面的参数是共享对象,也就是需要排队的对象
}
锁池:当运行有sychronized的代码时,需要去找锁(每个Java对象都有一把锁),从运行状态变为就绪状态,找不到等待,找到了,抢时间片再运行。
synchronized出现在静态方法上,是以类为对象的
synchronized出现在实例方法上是以this为对象
局部变量:在栈中
实例变量:在堆中
静态变量:在方法区
局部变量不会有线程安全问题
死锁
public class Thread_tset {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Object o1=new Object();
Object o2=new Object();
Thread t1=new MyThread1(o1,o2);
Thread t2=new MyThread2(o1,o2);
}
}
class MyThread1 extends Thread{
Object o1;
Object o2;
public MyThread1() {
}
public MyThread1(Object o1,Object o2) {
this.o1=o1;
this.o2=o2;
}
public void run() {
synchronized(o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
synchronized(o2) {
}
}
}
}
class MyThread2 extends Thread{
Object o1;
Object o2;
public MyThread2() {
}
public MyThread2(Object o1,Object o2) {
this.o1=o1;
this.o2=o2;
}
public void run() {
synchronized(o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
synchronized(o1) {
}
}
}
}
用户线程(后台线程):main线程
守护线程:一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束
例如,垃圾回收线程
定时器
间隔特定的时间,执行特定的程序
可以使用类库:java.util.Timer
现在使用较多的是SwingTask框架
Object的wait和notify方法
不是线程的方法
wait():
Object o=new Object();
o.wait();//让正在O对象上活动的线程进入等待状态,无期限等待,直到被唤醒,会使线程释放之前的锁
notify():
o.notify();//可以让正在o对象上等待的线程唤醒,只会通知不会释放锁。
notifyAll():唤醒所有正在o对象上等待的线程
waith和notify方法都必须建立在线程同步的基础上,因为必须保证数据安全问题