进程线程概念分析参考博客:https://blog.csdn.net/l1585931143/article/details/60610576
一个程序至少一个进程,一个进程至少一个线程。线程依托进程存在。
并行:时间段上来说多个程序运行
并发:时间点上来说程序运行,多个cpu实现程序的并发
jvm虚拟机启动是单线程还是多线程? 多线程
每个线程都有优先级
创建线程的方法有两种
1️⃣将类声明为Thread的子类,该子类重写Thread类的run方法,
线程中start和run的区别
start是使线程开始执行,java虚拟机调用该线程的run方法
run是对象调用方法,还是主线程在进行操作
public class ThreadDemo {
public static void main(String[] args) {
MyThread thread = new MyThread();
// thread.run();
thread.start();
System.out.println("over");
}
}
class MyThread extends Thread{
public void run(){
try {
sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
创建新线程并沉睡500ms,此时后面的system.out已经执行了(正经的start才会出现这种情况)
线程不能多次启动,会出现报错
Exception in thread "main" java.lang.IllegalThreadStateException
getName() 默认thread-0 获取线程名字
setName(String name) 设置线程名字
获取主线程对象
Thread thread2 = Thread.currentThread();
System.out.println(thread2.getName());
线程的阻塞方法的使用
join方法:等待线程运行完之后再运行。括号中还可以加时间,超过最大等待时间就开始运行
public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("TestJoin");
t1.start();
try {
t1.join(); //join()合并线程,子线程运行完之后,主线程才开始执行
}catch (InterruptedException e) { }
for(int i=0 ; i <10; i++)
System.out.println("I am Main Thread");
}
}
class MyThread2 extends Thread {
MyThread2(String s) {
super(s);
}
public void run() {
for(int i = 1; i <= 10; i++) {
System.out.println("I am "+getName());
try {
sleep(1000); //暂停,每一秒输出一次
}catch (InterruptedException e) {
return;
}
}
}
}
输出结果
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
yield()方法:暂停当前正在执行的线程对象,并执行其他线程
stop()方法已经过时
现在停止线程所用方法是使用标签
public class ThreadDemo3 {
public static void main(String[] args) {
Thread3 t = new Thread3();
t.start();
t.run();
}
}
class Thread3 extends Thread{
private boolean flag;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
if(i==3){
flag=false;
}
if(!flag){
return;
}
System.out.println(i);
}
}
}
符合停止条件时改变flag的值来实现让线程停止。
线程中断方法
interrupt():打破阻塞状态,中断线程,还会返回interruptException错误
配合join()的线程中断方法
//线程中断方法,配合join()
public class ThreadDemo4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 通过构造方法将主线程对象传递给子线程
Thread4 t = new Thread4(Thread.currentThread());
t.start();
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Thread.currentThread().interrupt();//放在这里无意义
System.out.println("over");
}
}
class Thread4 extends Thread {
private Thread mainThread;
public Thread4(Thread mainThread) {
this.mainThread = mainThread;
}
@Override
public void run() {
// 让主线程阻塞之后,再执行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
// 当i3的时候,中断线程
if (i == 3) {
mainThread.interrupt();
}
System.out.println(i);
}
}
}
配合sleep()的线程中断方法
public class ThreadDemo5 {
public static void main(String[] args) {
Thread5 t = new Thread5();
t.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.interrupt();
System.out.println("over");
}
}
class Thread5 extends Thread {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
线程的生命周期
线程先start启动,因为没有cpu执行权,所以没有执行资格,抢到cpu执行权,调用线程的方法,此时run,stop会线程执行结束,sleep,join,wait会进入阻塞状态,等待cpu
2️⃣创建线程的另一种方式是声明实现Runnable()接口的类,该类实现run方法,然后可以分配该类的实例,在创建thread的时候作为一个参数来传递并启动。
多线程可以执行一个Runnable任务
public class ThreadDemo6 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable task = new MyRunnable();
// 多个线程执行同一个Runnable任务
new Thread(task).start();
new Thread(task).start();
}
}
class MyRunnable implements Runnable{
private int count=10;
@Override
public void run() {
// TODO Auto-generated method stub
while(count>0){
System.out.println(count);
count--;
}
}
}
有资源共享,就用Runable方法
多线程的线程安全问题及同步代码块,同步方法相关
出现线程安全问题的原因?
多线程环境下且存在共享数据,同时共享数据被多条语句引用
解决线程安全问题的方法?
让操作共享数据的代码在任意时间只能被同一个线程所操作
可以使用同步代码块实现:synchronized(锁对象){
存在线程安全的代码
}
同步代码块的优缺点
优点:解决了线程安全问题
缺点:每次运行的时候都要检查锁对象是否被释放
同步方法:
在run方法前面修饰符加上synchronized,将整个方法的代码都锁起来
非静态同步方法的锁对象又是什么呢?this
静态方法的锁对象是:当前类的字节码文件对象
锁对象:锁对象可以是任意对象,多个线程必须使用的是同一把锁,也就是说必须使用同一个对象
锁对象什么时候会释放?
1.同步代码执行完毕
2.线程进入等待状态
3.线程停止
什么时候使用同步代码块,什么时候使用同步方法?
当方法里面只有一部分代码存在安全问题时候,使用同步代码块
当方法里面所有的代码都存在安全问题的时候切当前锁对象可以this时使用同步方法
实现Runnnable接口的创建线程可以使用同步代码块和同步方法
继承Thread类的创建线程只能使用同步代码块,不能使用同步方法
售票程序
public class ThreadTest {
public static void main(String[] args) {
/*
* 线程同步安全问题出现的根据: 多线程环境 多线程操作共享数据 操作数据的语句是多行
* 线程安全问题的解决方法:让操作共享数据的代码在同一时间只能被同一个线程所操作
*/
// 集成Thread方式
// TicketThread t = new TicketThread("窗口1");
// t.start();
// TicketThread t2 = new TicketThread("窗口2");
// t2.start();
// TicketThread t3 = new TicketThread("窗口3");
// t3.start();
// 实现Runnable接口的方式
TicketRunnable ticket = new TicketRunnable();
new Thread(ticket, "窗口1").start();
new Thread(ticket, "窗口2").start();
new Thread(ticket, "窗口3").start();
}
}
继承Thread类
public class TicketThread extends Thread {
// 所有进程共享资源是票,必须加static
private static int ticket = 100;
public TicketThread(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void run() {
synchronized (Object.class) {
while (ticket > 0) {
System.out.println(getName() + "正在出售第" + ticket + "张票");
ticket--;
}
}
}
}
实现Runnable接口
public class TicketRunnable implements Runnable {
private int ticket = 100;
@Override
public void run() {
// TODO Auto-generated method stub
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
ticket--;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
火车进站小程序
线程方法:
public final void wait() 使线程等待
public final void notify() 随机唤醒等待的其中一个线程
public final void notifyAll() 唤醒所有等待中的线程
public void interrupt() 可以中断等待的线程状态
!!!!上述方法必须在同步代码里面通过锁对象调用object.class.调用
为什么定义在object类中?
因为锁对象可以是任意对象,又因为wait,notify,notifyAll必须通过锁对象调用,即任意对象都可以调用这些方法,同时又因为任意对象都可以调用的方法应该定义在Object类中,所以定义在Object类中。
public class EnterTrainDemo {
public static void main(String[] args) {
EnterTrainRunnable passenger = new EnterTrainRunnable();
new Thread(passenger,"乘客1").start();
new Thread(passenger,"乘客2").start();
new Thread(passenger,"乘客3").start();
new Thread(passenger,"乘客4").start();
//等到乘客都进站了,进站之后再唤醒乘客
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
passenger.setOpen(123456);
new Thread(passenger,"工作人员").start();
}
}
class EnterTrainRunnable implements Runnable{
private boolean isOpen;
public void setOpen(int key){
if(key==123456){
isOpen=true;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (Object.class) {
if(isOpen){
isOpen=false;
System.out.println(getThreadName()+"进站了,5秒钟后放行乘客");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Object.class.notify();//随机唤醒一个乘客
Object.class.notifyAll();
}else{
System.out.println(getThreadName()+"正在等待");
try {
Object.class.wait();//线程进入等待状态,锁对象会释放
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getThreadName()+"被放行了");
}
}
}
private String getThreadName() {
// TODO Auto-generated method stub
return Thread.currentThread().getName();
}
}
关于锁Lock
public class TicketRunnable implements Runnable {
private int ticket = 100;
private Lock lock=new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
lock.lock();
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
ticket--;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
lock.unlock();
}
}
尽量多个线程使用同一把锁,可以避免死锁。
线程池:为了防止频繁创建销毁线程浪费系统资源而创建的线程容器
全是静态成员方法,是个工具类。
从JDK1.5之后,java内置线程池
三种创建线程池的方法
1️⃣创建一个存活60s的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(myRunnable);
2️⃣创建一个只放一个线程的线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
threadPool.execute(myRunnable);
3️⃣创建一个指定线程数的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3)
threadPool.execute(myRunnable);
线程池的关闭
threadPool.shutdown()
自定义线程池工具类
public class ThreadPoolUtils {
public static void execute(Runnable task) {
private static ExecutorService threadPool = Executors.newCachedThreadPool();
public static void execute(Runnable task){
threadPool.execute(task);
}
}
}
匿名内部类创建线程的两种方式
public class ThreadDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
线程的定时器:可用于调度多个定时任务以后台线程的方式执行。可安排任务执行一次,或者定期重复执行
public class TimerDemo {
public static void main(String[] args) {
// 创建一个新的定时器
Timer timer = new Timer();
// 要被执行的任务
MyTimerTask mytimerTask = new MyTimerTask();
// 安排在制定延迟后执行指定任务,多长时间之后任务开始执行,执行之后每隔多久开始执行下一个
timer.schedule(mytimerTask, 1000,1000);
timer.cancel();
}
}
class MyTimerTask extends TimerTask{
@Override
public void run() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(new Date()));
// 当任务执行完毕之后,关闭定时器
}
}
同步有三种方式
同步代码块:锁对象可以使任意对象,所以用object。
同步方法:锁对象是this,关键字放在方法的修饰符位置上
使用lock子类对象:创建ReentrantLock子类对象然后使用lock和unlock分别进行加锁和解锁。
run()和start()的区别
run()没有创建新的线程。start()创建了新的线程。
sleep()和wait()方法的区别?
sleep是睡眠指定时间,等指定时间到了就苏醒
wait:只能在同步代码中通过锁对象调用,必须通过锁对象唤醒。