文章目录
线程
什么是进程?什么是线程?
进程是一个应用程序(1个进程就是一个软件)。
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程。
例如
对于java程序来说,当在DOS命令窗口输入:java HelloWorld回车之后。
会先启动JVM(就是一个进程),JVM再启动一个主线程调用main方法,同时再启动一个垃圾回收线程负责看护,回收垃圾
则Java程序中至少有两个线程并发
进程之间是独立的,不共享内存资源
同一进程的不同线程,堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈
线程的一些基本操作
创建线程
创建线程方式一:
编写一个类(线程类),直接继承java.lang.Thread,重写run方法
注意:线程开启不一定立即执行,有cpu调度执行
public class ThreadTest01 {
public static void main(String[] args) {
//创建一个线程的对象
MyThread t = new MyThread();
//启动线程
/*
start()方法的作用:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束
只要栈空间开出来,start方法就结束了(线程就启动成功了)
启动成功的线程会自动调用run方法,并且run方法在分支栈的底部
run方法在分支栈的栈底部,main方法在主栈的栈底部
run和main平级
*/
t.start();
/*t.run();//这不会启动分支线程(依然是单线程,只是调用了一个方法)*/
//接下来继续运行主线程
for (int i = 0; i < 100; i++) {
System.out.println("主栈进程"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支栈进程:"+i);
}
}
}
创建线程的第二种方式:
实现线程的第二种方式,编写一个类实现java.lang.Runnable接口
推荐这种写法,因为java是单继承(局限性),可以实现多个接口,更加灵活
public class ThreadTest02 {
public static void main(String[] args) {
//创建一个可运行的对象
MyRunnable r = new MyRunnable();
//将可运行的对象封装成一个线程对象
//Thread是一个线程类
Thread t = new Thread(r);
//启动线程
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程->"+i);
}
}
}
//并不是一个线程类,是一个实现Runnable接口的一个可运行类
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程->"+i);
}
}
}
1、怎么获取当前线程对象?
Thread t = Thread.currentThread();
返回值t就是当前线程
2、获取线程对象的名字
String name = 线程对象.getName();
3、修改线程的名字
线程对象.setName("线程名字");
4、当前线程的默认名字:
Thread-0
Thread-1
Thread-2
Thread-3
Thread-4
…
守护线程
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t1 = new Thread(god);
t1.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户线程
t1.start();//上帝 守护线程启动
new Thread(you).start();//你 用户线程启动
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){//理论上不会停止
System.out.println("上帝守护着你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("开心的一天");
}
System.out.println("========Bye++++++++++++");
}
}
线程的插队(join)
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
TestJoin tj = new TestJoin();
Thread t =new Thread(tj);
t.start();
//主线程
for (int i = 0; i < 1000; i++) {
if(i==200){
t.join();//插队
}
System.out.println("main"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("vip线程插队了"+i);
}
}
}
线程的优先级
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,都要看cpu的调度
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority mp = new MyPriority();
Thread t1 = new Thread(mp);
Thread t2 = new Thread(mp);
Thread t3 = new Thread(mp);
Thread t4 = new Thread(mp);
Thread t5 = new Thread(mp);
//先设置优先级,再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(8);
t4.start();
t5.setPriority(6);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
Thread的静态方法sleep
模拟倒计时
public class TestSleep {
public static void main(String[] args) {
try {
down(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void down(int n) throws InterruptedException {
while(true){
Thread.sleep(1000);
System.out.println(n--);
if(n<=0){
break;
}
}
}
}
线程的正常停止
1、建议线程正常停止--->利用次数,不建议死循环
2、建议使用标志位--->设置一个标志位
3、不要使用stop或者destroy等过时或者JDK不建议使用的方法
public class TestStop implements Runnable {
//1、设置一个标志位
private boolean flag = true;
//2、设置一个方法停止线程,转换标志位
public void myStop(){
this.flag = false;
}
//main线程
public static void main(String[] args) {
TestStop ts = new TestStop();
new Thread(ts).start();
for (int i = 0; i < 1000; i++) {
if(i==900){
//调用stop方法切换标志位,让run线程停止
ts.myStop();
System.out.println("run线程停止");
}
System.out.println("main--Thread->"+i);
}
}
//run线程
@Override
public void run() {
int i =0;
while(flag){
System.out.println("run--Thread->"+i++);
}
}
}
线程让位(yield)
礼让不一定会成功,看cpu心情
public class TestYield {
public static void main(String[] args) {
MyYield my = new MyYield();
new Thread(my,"a").start();
new Thread(my,"b").start();
}
}
//创建一个线程类
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程安全问题
1、什么时候数据在多线程并发的环境下会存在安全问题?
三个条件
条件1:多线程并发
条件2:有共享数据
条件3:共享数据有修改的行为
2、怎么解决线程安全问题?
线程排序执行(不能并发)
这种机制被称为:线程同步机制
线程同步就是线程排队,会牺牲一部分效率,但是值得的,数据安全是前提,数据安全才有资格谈效率。
处理多线程问题,多个线程访问同一个对象
这是我们就需要线程同步(一种等待机制),多个需要访问同一对象的线程进入这个对象的等待池形成队列
,为了保证数据在访问时的正确性,在访问时加入了锁机制synchronized,当一个线程获得了对象的排他锁
,就独占资源,其他线程必须等待
public class Demo01 {
public static void main(String[] args) {
BuyTicket bt = new BuyTicket();
new Thread(bt,"小王").start();
new Thread(bt,"老黑").start();
new Thread(bt,"黄牛").start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNums = 10;
//标志位
boolean flag = true;
@Override
public void run() {
//买票
while(flag){
buy();
}
}
//synchronized同步方法,所得是this
//buy的时候在线程池中等待
private synchronized void buy(){
//判断是否有票
if(ticketNums <= 0){
flag = false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
线程状态(线程的生命周期)
生产者消费者模式
关于Object类中的wait和notify方法(生产者和消费者模式!)
第一:
wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法
wait方法和notify方法不是通过线程对象调用的
第二:
wait方法的作用?
Object o = new Object();
o.wait();
表示:
让正在o对象上活动的线程进入等待状态,无期限等待
直到被唤醒为止。
第三:
notify()方法的作用?
Object o = new Object();
o.notify();
表示:
唤醒正在o对象上等待的线程。
还有一个notifyAll()方法:
这个方法是唤醒o对象上处于等待的所有线程