目录
一,简介
1,什么是线程
线程是程序执行的一条路径, 一个进程中可以包含多条线程
多线程并发执行可以提高程序的效率, 可以同时完成多项工作
有时称为轻量级进程,是CPU使用的基本单元;
2,线程组成
由线程ID、程序计数器、寄存器集合和堆栈组成。
3,多线程的应用场景
迅雷开启多条线程一起下载
QQ同时和多个人一起视频
服务器同时处理多个客户端请求
4,并发和并行的区别
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
比如我跟两个网友聊天,左手操作一个手机跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
如果用一部手机先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。
二,线程分类
Java线程主要分为:用户线程(User Thread)和守护线程(Daemon Thread)
2.1,守护线程
该线程不会单独执行, 当其他非守护线程(用户线程)都执行结束后, 自动退出;
设置守护线程的方法:
//设置该线程为守护线程
thread.setDaemon(true);//注意必须在线程启动start()方法之前调用。
应用:垃圾回收线程
2.2,用户线程
主要包括:主线程和子线程(用户线程),子线程设置了setDaemon(true);就是守护线程;
注意:不能把主线程设置为守护线程,不然会报IllegalThreadStateException异常;
2.3,区别
用户线程的优先级高,守护线程的优先级低;
如果JVM中还存在用户线程,那么JVM就会一直存活,不会退出;但是JVM中只有守护线程的时候,JVM就会退出;
守护线程依赖于用户线程,JVM中所有用户线程退出了,所有的守护线程也就会退出 ;
总之,只要有用户线程存在,JVM就不会退出;所有用户线程都退出了,JVM也就退出了,JVM都退出了,守护线程也就退出了;
注意:以下代码的逻辑让守护线程提前于用户线程消亡的情况下,守护线程并不会主动延长生命和用户线程一起消亡。
public static void main(String[] args) {
final Thread t = new Thread(){
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("守护线程");
};
};
t.setDaemon(true);
t.start();
Thread t2 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("用户线程 " + " 守护线程是否存活" + t.isAlive());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
};
t2.start();
}
但是让守护线程延迟于用户线程消亡的情况下,守护线程会提前和用户线程一起消亡。
三,多线程实现方式
3.1,通过继承Thread类,开启一个线程,实现多线程;
public class ThreadDemo {
public static void main(String[] args) {
ThreadTest thTest = new ThreadTest();
thTest.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是主线程");
}
}
}
class ThreadTest extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for (int i = 0; i < 1000; i++) {
System.out.println("子线程");
}
}
}
以上通过打印信息可以看到,主线成和子线程是交替执行的,说明有多线程在运行;
3.2,通过实现Runnable接口
public class ThreadDemo2 {
private void mian() {
// TODO Auto-generated method stub
MyRunnable r = new MyRunnable();
Thread t= new Thread(r);
t.start();
}
}
class MyRunnable implements Runnable{
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<1000;i++){
System.out.println("我是子线程");
}
}
}
3.3,匿名内部类实现,就是以上两种方式的另一种写法;
public class ThreadDemo3 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("匿名内部类继承Thread,开启一个线程");
}
}.start();
new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
System.out.println("匿名内部类实现Runnable接口,开启一个线程");
}
}).start();
}
}
四,线程常用方法
4.1,获取线程的名字:getName();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println(this.getName() + " aaaaaa");
}
}.start();
}
4.2,为线程设置名字,不设置有默认的名称:setName()
//第一种方式
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
this.setName("android ");
System.out.println("当前线程名字:"+this.getName())
}
}.start();
//第二种方式,构造方法中为线程设置一个名字
new Thread("Windows"){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("当前线程名字:"+this.getName())
}
}.start();
}
4.3,获取当前线程对象:Thread.currentThread
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
Thread.currentThread().setName("Window");
}
}).start();
//获取主线程的名字
Thread.currentThread.getName();
}
4.4,让线程休眠的方法:Thread.sleep(休眠时间,单位毫秒);
4.5,join()线程加入方法:让当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续执行;
public static void main(String[] args) {
final Thread t1 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName()+ " aaaaaaa");
}
};
};
Thread t2 = new Thread(){
public void run() {
for (int i = 0; i < 10; i++) {
if(i==2){
try {
t1.join();//t1线程加入,t2等待t1执行完成再继续执行;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.getName()+ " bbbbbbbbbbbbbbbbbb");
}
};
};
t1.start();
t2.start();
}
打印输出:
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
4.6,setPriority()设置线程优先级
优先级从0(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)范围内;
默认优先级:Thread.NORM_PRIORITY=5
4.7,yield() 设置线程礼让,让出cpu,让其他线程执行;
五,线程同步和线程安全
https://blog.youkuaiyun.com/ezconn/article/details/100060812
六,线程间通信
6.1,什么时候需要通信
多个线程并发执行时, 在默认情况下CPU是随机切换线程的
如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
6.2,两个线程间的通信
通过wait()和notify()方法实现,wait()方法让当前线程等待,notify()唤醒正在等待的线程;
例如:两个线程实现循环重复打印两句诗
实现:
public class ThreadDemo1 {
public static void main(String[] args) {
final Printer p = new Printer();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer{
int flag = 0;
public synchronized void print1() throws InterruptedException{//同步方法默认的锁对象是this
if(flag!=0){
this.wait();//同步锁对象调用wait()
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 2;
this.notify();//同步锁对象调用notify()
}
public synchronized void print2() throws InterruptedException{//同步方法默认的锁对象是this
if(flag!=2){
this.wait(); //同步锁对象调用wait()
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 1;
this.notify();//同步锁对象调用notify()
}
}
注意:wait()和notify()这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
6.3,三个或者三个以上的线程通信
通过wait()让当前线程等待,notifyAll()方法是唤醒所有线程;
public class ThreadDemo2 {
public static void main(String[] args) {
final Printer2 p = new Printer2();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i =0;
while (i<1000) {
i++;
try {
p.print4();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer2{
int flag = 0;
public synchronized void print1() throws InterruptedException{
while(flag!=0){
this.wait();
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 1;
this.notifyAll(); //
}
public synchronized void print2() throws InterruptedException{
while(flag!=1){
this.wait();
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 2;
this.notifyAll();
}
public synchronized void print3() throws InterruptedException{
while(flag!=2){
this.wait();
}
System.out.print("举");
System.out.print("头");
System.out.print("望");
System.out.print("明");
System.out.print("月");
System.out.print("\r\n");
flag = 3;
this.notifyAll();
}
public synchronized void print4() throws InterruptedException{
while(flag!=3){
this.wait();
}
System.out.print("低");
System.out.print("头");
System.out.print("思");
System.out.print("故");
System.out.print("乡");
System.out.print("\r\n");
flag = 0;
this.notifyAll();
}
}
6.4,JDK1.5之前通过上面的方式,实现多个线程间通信,1.5版本增加了一个互斥锁ReentrantLock实现;
使用ReentrantLock类中的lock()和unLock()方法替代synchronized实现同步;然后再ReentrantLock创建Condition(监视器)子类对象,每个线程中放一个Condition;通过Condition的await()和signal() 方法实现当前线程等待和唤醒指定的线程;await()相当于Object中的wait()方法;signal()和Object中的notify()和notifyAll()方法有点不一样;
例如:
public class fThreadDemo3 {
public static void main(String[] args) {
final Printer3 p = new Printer3();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
int i = 0;
while (i < 1000) {
i++;
try {
p.print4();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer3 {
int flag = 0;
ReentrantLock rl = new ReentrantLock();
Condition c1 = rl.newCondition();
Condition c2 = rl.newCondition();
Condition c3 = rl.newCondition();
Condition c4 = rl.newCondition();
public void print1() throws InterruptedException {
rl.lock();
try {
if (flag != 0) {
c1.await();
}
System.out.print("窗");
System.out.print("前");
System.out.print("明");
System.out.print("月");
System.out.print("光");
System.out.print("\r\n");
flag = 1;
c2.signal();
} finally{
rl.unlock();
}
}
public void print2() throws InterruptedException {
rl.lock();
try {
if (flag != 1) {
c2.await();
}
System.out.print("疑");
System.out.print("是");
System.out.print("地");
System.out.print("上");
System.out.print("霜");
System.out.print("\r\n");
flag = 2;
c3.signal();
} finally {
rl.unlock();
}
}
public void print3() throws InterruptedException {
rl.lock();
try {
if (flag != 2) {
c3.await();
}
System.out.print("举");
System.out.print("头");
System.out.print("望");
System.out.print("明");
System.out.print("月");
System.out.print("\r\n");
flag = 3;
c4.signal();
} finally{
rl.unlock();
}
}
public void print4() throws InterruptedException {
rl.lock();
try {
if (flag != 3) {
c4.await();
}
System.out.print("低");
System.out.print("头");
System.out.print("思");
System.out.print("故");
System.out.print("乡");
System.out.print("\r\n");
flag = 0;
c1.signal();
} finally {
rl.unlock();
}
}
}