多线程
线程和进程
进程:一个独立的正在执行的程序
线程:一个进程的最基本执行单元,执行路径
多进程:在操作系统中 同时运行多个程序
多进程的好处:可以充分利用CPU,提高CPU的使用率
多线程:在同一进程(应用程序)中同时执行多个线程
开启多线程的好处
提高进程的执行使用率,提高CPU的使用率
执行一些耗时操作,例如访问网络,上传下载文件,访问数据库,更新界面等等,防止出现线程阻塞的现象。
注意
1.在同一个时间点一个CPU中只可能有一个线程在执行。
2.多线程不能提高效率,反而会降低效率,但是可以提高CPU的使用率。
3.一个进程如果有多条执行路径,则称为多线程程序。
4.Java虚拟机的启动至少开启了两条线程,主线程和垃圾回收线程。
5.一个线程可以理解为进程的子任务。
其实多线程本质还是CPU按照顺序流来处理,只不过CPU的执行速度太快了,我们肉眼察觉不到,所以看起来像同时执行
多线程的实现
方式一:继承Thread类
自定义类MyThread继承Thread类。- ->MyThread类里面重写run()方法。- ->创建线程对象。- ->启动线程。
public class ThreadDemo01 {
public static void main(String[] args) {
//创建线程对象
ThreadTest01 tdt = new ThreadTest01();
//启动线程。注意这里不是tdt.run();而是tdt.start();。
//tdt.run()就是普通的调方法
tdt.start();
for(int i=0 ;i<1000;i++) {
System.out.println("主线程"+"-->"+i);
}
}
}
//继承Thread类
class ThreadTest01 extends Thread{
//重写run()方法
@Override
public void run() {
for(int i=0 ;i<1000;i++) {
System.out.println("子线程"+"-->"+i);
}
}
}
部分运行结果如下:交替进行
方式二:实现Runnable接口
自定义类MyRunnable实现Runnable接口 - -> 重写run()方法 - -> 创建MyRunnable类的对象 - -> 创建Thread类的对象,并把步骤3创建的对象作为构造参数传递 - -> 启动线程
实现接口方式的好处
1.可以避免由于Java单继承带来的局限性。
2.适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
public class ThreadDemo03 {
public static void main(String[] args) {
// 3.创建MyRunnable类的对象
MyRunnable mr = new MyRunnable();
// 4.创建Thread类的对象,并把步骤3创建的对象作为构造参数传递
Thread t = new Thread(mr);
// 5.启动线程
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程: " + i);
}
}
}
class MyRunnable implements Runnable {
// 2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("子线程: " + i);
}
}
}
方式三:实现Callable接口
实现Runnable和实现Callable接口的区别
1.实现Callable接口有返回值
2.实现Callable接口可以声明异常
public class ThreadDemo04 {
public static void main(String[] args) {
FutureTask<Integer> task = new FutureTask<>(new MyCallable(1,100));
Thread t = new Thread(task);
t.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main: " + i);
}
try {
Integer value = task.get();
System.out.println(value);
} catch (InterruptedException | ExecutionException e) {
System.out.println("子线程抛出异常给主线程: " + e);
// e.printStackTrace();
}
System.out.println("over");
}
}
/*
* 计算m~n的和
*/
class MyCallable implements Callable<Integer> {
private Integer m;
private Integer n;
public MyCallable() {
super();
}
public MyCallable(Integer m, Integer n) {
super();
this.m = m;
this.n = n;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = m; i <= n; i++) {
System.out.println("Callable: " + i);
sum += i;
}
throw new NullPointerException();
// return sum;
}
public Integer getM() {
return m;
}
public void setM(Integer m) {
this.m = m;
}
public Integer getN() {
return n;
}
public void setN(Integer n) {
this.n = n;
}
}
线程的常用方法
构造方法
Thread(String name) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象并命名。
常用的成员方法
public final String getName() 获取线程的名字
public final void setName(String name) 设置线程的名字
public final int getPriority() 获取线程的优先级
public final void setPriority(int newPriority) 设置线程的优先级
public void interrupt() 中断线程
public final void setDaemon(boolean on) 设置为后台线程
public final void join() 线程加入
public static void yield() 线程礼让
常用的静态方法
Thread.currentThread().getName();可以获取任意方法所在的线程名称
public static void sleep(long millis) 线程休眠
设置和获取线程名称
public class ThreadDemo01 {
public static void main(String[] args) {
// MyThread t1 = new MyThread("子线程1");
// MyThread t2 = new MyThread("子线程2");
// MyThread t1 = new MyThread();
// MyThread t2 = new MyThread();
//
// t1.setName("子线程3");
// t2.setName("子线程4");
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr, "子线程5");
Thread t2 = new Thread(mr, "子线程6");
Thread t3 = new Thread(mr, "子线程7");
System.out.println(t1.getId());
System.out.println(t2.getId());
System.out.println(t3.getId());
t1.start();
t2.start();
// Thread currentThread = Thread.currentThread();
// System.out.println(currentThread.getName());
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
class MyThread extends Thread {
public MyThread() {
super();
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
获取和设置优先级
public class ThreadDemo02 {
public static void main(String[] args) {
PriorityThread pt = new PriorityThread();
Thread t1 = new Thread(pt, "刘备");
Thread t2 = new Thread(pt, "关羽");
Thread t3 = new Thread(pt, "张飞");
// System.out.println(t1.getPriority());
// System.out.println(t2.getPriority());
// System.out.println(t3.getPriority());
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
class PriorityThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
线程休眠
public class ThreadDemo03 {
public static void main(String[] args) {
Thread t = new Thread(new SleepThread(), "时钟线程");
t.start();
}
}
// 利用线程休眠模拟时钟
class SleepThread implements Runnable {
@Override
public void run() {
while (true) {
String dateStr = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date());
System.out.println(Thread.currentThread().getName() + "的当前时间为: " + dateStr);
// 休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
中断线程
public class ThreadDemo04 {
public static void main(String[] args) {
Thread t = new Thread(new StopThread(), "子线程");
t.start();
try {
Thread.sleep(3000);
//t.stop(); 已过时
//stop和interrupt的区别
//stop:结束线程的生命不会有提示
//interrupt: 会给线程抛出一个中断异常,对应的线程可以处理或者结束
t.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class StopThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "睡眠起始时间:" + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("有人打断我了");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "睡眠结束时间:" + new Date());
}
}
后台线程
一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程。
所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,
并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束的时候,
也就是用户线程都结束的时候,程序也就终止了。同时,会杀死进程中的所有的后台线程。
反过来说,只要有任何非后台线程还在运行,程序就不会结束。比如执行main()的就是一个非后台线程。基于这个特点,当虚拟机中的用户线程全部退出运行时,守护线程没有服务的对象后,JVM也就退出了。
public class ThreadDemo05 {
public static void main(String[] args) {
DaemonThread t1 = new DaemonThread();
DaemonThread t2 = new DaemonThread();
DaemonThread t3 = new DaemonThread();
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.setDaemon(true);
t2.setDaemon(true);
t3.setDaemon(true);
t1.start();
t2.start();
t3.start();
Thread.currentThread().setName("大水晶");
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
class DaemonThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
线程加入
public class ThreadDemo06 {
public static void main(String[] args) {
JoinThread t1 = new JoinThread();
JoinThread t2 = new JoinThread();
JoinThread t3 = new JoinThread();
t1.setName("老张");
t2.setName("老李");
t3.setName("老王");
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t3.start();
//如果像这样写线程会按顺序执行
}
}
class JoinThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
线程礼让
/*
让出CPU的执行权一小会
这里让出了CPU的执行权,并不是说把执行权交割了其他线程
而是释放了自己的执行权,自己还可以重新和其他线程抢夺执行权
*/
public class ThreadDemo07 {
public static void main(String[] args) {
YieldThread t1 = new YieldThread();
YieldThread t2 = new YieldThread();
YieldThread t3 = new YieldThread();
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
class YieldThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + ":" + i);
Thread.yield();
}
}
}
线程的生命周期
新建状态(New)
- 新创建了一个线程对象。
就绪状态(Runnable)
- 线程对象创建后,其他线程调用了该对象的start()方法。该状态 的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
运行状态(Running)
- 就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked)
- 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种
等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead)
- 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程同步
在多线程环境下,至少有两条以上的原子性语句操作了共享数据,并且这个操作是写操作,肯定会出现线程安全问题,如何来解决线程安全问题呢?
1.同步代码块
格式:
synchronized(锁对象){需要同步的代码;}
注意:
a.锁对象是任意对象
b.不同线程共享同一把锁
c.同步方法的锁对象是 this
d.如果方法是静态方法,锁对象是字节码文件对象
同步的好处:解决了多线程的安全问题。
同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,降低程序的运行效率。如果出现了同步嵌套,就容易产生死锁问题2.同步方法
2.同步方法
格式:
public synchronized 返回值 方法名(参数列表) {
//需要同步的代码块
}
3.Lock锁
public class RannableDemo03 {
public static void main(String[] args) {
RunableTest03 rt = new RunableTest03();
Thread td1 = new Thread(rt,"Runnable线程1");
Thread td2 = new Thread(rt,"Runnable线程2");
Thread td3 = new Thread(rt,"Runnable线程3");
td1.start();
td2.start();
td3.start();
}
}
class RunableTest03 implements Runnable{
static int i=10000;
@Override
public void run() {
while(i>0) {
Test01();
}
}
/*
2.同步方法
格式:
public synchronized 返回值 方法名(参数列表) {
//需要同步的代码块
}
*/
public synchronized void Test01() {
if(i>0) {
String str =Thread.currentThread().getName()+"-->"+i--;
try(BufferedWriter bw = new BufferedWriter(new FileWriter("RunnableDem002.txt",true));) {
System.out.println(str);
bw.append(str);
bw.newLine();
bw.flush();
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class RunnableDemo02 {
public static void main(String[] args) {
RunableTest02 rt = new RunableTest02();
Thread td1 = new Thread(rt,"Runnable线程1");
Thread td2 = new Thread(rt,"Runnable线程2");
Thread td3 = new Thread(rt,"Runnable线程3");
td1.start();
td2.start();
td3.start();
}
}
//3.Lock锁
class RunableTest02 implements Runnable{
Lock lock = new ReentrantLock();
static int i=10000;
@Override
public void run() {
while(true) {
//3.Lock锁
lock.lock();
if(i>0) {
String str =Thread.currentThread().getName()+"-->"+i--;
try(BufferedWriter bw = new BufferedWriter(new FileWriter("RunnableDem002.txt",true));) {
System.out.println(str);
bw.append(str);
bw.newLine();
bw.flush();
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
break;
}
lock.unlock();
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
ThreadTest02 th1 = new ThreadTest02();
th1.setName("线程1");
ThreadTest02 th2 = new ThreadTest02();
th2.setName("线程2");
ThreadTest02 th3 = new ThreadTest02();
th3.setName("线程3");
th1.start();
th2.start();
th3.start();
}
}
/*
1.同步代码块
格式:
synchronized(锁对象){需要同步的代码;}
*/
class ThreadTest02 extends Thread{
static int i=100000;
@Override
public void run() {
while(true) {
synchronized(MyLook.look) {
if(i>0) {
String str =Thread.currentThread().getName()+"-->"+i--;
try(BufferedWriter bw = new BufferedWriter(new FileWriter("RunnableDem002.txt",true));) {
System.out.println(str);
bw.append(str);
bw.newLine();
bw.flush();
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
class MyLook{
public static final Object look =new Object();
}