线程
1.程序、线程、进程
程序:为完成某种功能,使用计算机语言编写的一系列指令集,也就是静态代码
进程:运行中的程序,计算机操作系统进行资源分配的最小单位
线程:是进程内部最小的执行单元,是操作系统进行任务调度的最小单元
2.进程与线程之间的关系
(1).一个进程包含多个线程,但一个线程只属于一个进程
(2).一个进程中必须有一个主线程,在主线程中可以创建其他子线程
//main方法为主线程
public static void main(String[] args) {
//创建一个新的线程
MyThread mThread=new MyThread();
//mThread.test(); //直接调用新线程中的方法无法启动线程,相当于普通方法的调用
mThread.start(); //启动新线程
for(int i=0;i<1000;i++){
System.out.println("主线程:"+i);
}
}
(3).一个进程中的线程可以共享该进程中的所有资源
3.Java
中如何创建线程?
(1).创建一个类继承Thread
类(该类属于线程类)
public class MyThread extends Thread{
//创建新线程必须继承Thread类,重写run()方法,将线程中需要执行的内容写在run()中
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("新线程:"+i);
}
}
}
(2).创建一个类实现Runnable
接口(该类不属于线程类)
public class RunTask implements Runnable{
//创建一个任务执行类,实现Runnable接口,重写run()方法
@Override
public void run() {
//需要执行的任务
for(int i=0;i<200;i++){
System.out.println("runtask:"+i);
}
}
}
public class Test {
//创建主线程
public static void main(String[] args) {
RunTask task=new RunTask(); //创建一个任务执行的对象
Thread thread=new Thread(task); //创建一个新的线程对象,添加需要执行的任务
thread.start(); //启动新线程
for(int i=0;i<200;i++){
System.out.println("main:"+i);
}
}
}
Note:类实现Runnble
接口,必须重写run()方法,将需要执行的任务写在run()中!
4.两种创建方式有什么区别?
(1).所创建的Thread子类不能再继承其他类(Java是单继承)
(2).创建一个类实现Runnable接口,该类还能再继承其他类
5.Thread
类中的方法
run(); //写线程中需要执行的代码
start(); //启动线程
Thread thread-new Thread(); //创建一个线程
Thread thread=new Thread(task); //创建一个线程并添加需要执行任务
Thread thread=new Thread(task,"MyThread1"); //在线程中添加需要执行的任务并为其设置线程名
thread.setName(String name); //为线程设置线程名
thread.setPriority(int Priority); //为线程设置优先级,1-10
Thread.currentThread(); //获取当前正在执行的线程
Thread.currentThread().getName(); //返回当前正在执行的线程的名字
Thread.currentThread().getId(); //返回当前正在执行的线程的ID
Thread.currentThread().getPriority(); //返回当前正在执行的线程的优先级,默认为5
Thread.yield(); //线程让步,使线程从运行状态转为就绪状态
Thread.sleep(); //线程休眠指定时间,即线程从运行状态转为阻塞状态,不会释放锁,时间完后又转为就绪状态
Thread.join(); //线程加入,使其他线程先转为阻塞状态,等待该线程执行结束后再执行
wait(); //线程等待,使线程进入阻塞状态,自己无法唤醒,等待notify()或notifyAll()唤醒,等待过程中会自动释放锁
5.线程状态(线程生命周期)
6.守护线程
线程分为用户线程和守护线程
(1).守护线程的设置
DaemonThread dt=new DaemonThread(); //创建DaemonThread类的对象,该类实现Runnable接口
Thread thread=new Thread(dt);
thread.setDaemon(true); //设置线程为守护线程,必须在线程启动之前进行设置
thread.start();
(2).作用:为其他线程提供服务
(3).特点:在系统中其他线程结束运行后,守护线程会自动结束运行,如JVM
中的垃圾处理线程就是守护线程
7.多线程
(1).什么是多线程?
在一个程序中可以创建多个线程,执行不同的任务
(2).什么时候需要多线程?
程序需要同时执行多个任务时
(3).多线程的优点
提高程序的相应;提高CPU的利用率;改善程序的结构
(4).多线程的缺点
占用内存;
占用CPU资源,需要对多线程进行管理;
多个线程同时访问共享资源时,如果不加以控制,可能会出现线程安全问题
8.线程同步
对于线程安全问题,Java中引入了线程同步,通过对多个线程访问共享资源时进行加锁,使多个线程依次访问共享资源,从而避免出现线程安全问题
(1).并行与并发
并行:多个线程在同一时刻同时执行
并发:在一个时间段内,多个线程一次执行
(2).加锁方式
synchorized
关键字修饰:
1>.修饰代码块时,需要创建同步对象(多个线程对应一个同步对象,用来记录是否有线程进入到同步代码块中)
对于Thread类的子类,如果创建多个子类对象(即创建了多个线程),在使用synchronized关键字对run()中需要执行的代码块进行修饰时,所创建的同步对象需要使用关键字static修饰,确保多个线程对应的是一个同步对象
//模拟购票案例
public class TicketThread1 extends Thread{
static int num=10; //票的数量(共享数据)
static Object obj=new Object(); //锁对象使用static修饰
@Override
public void run() {
while(true){
//注意加锁的位置,在While循环内
synchronized(obj){
//obj锁对象必须是同一个对象,记录每次执行的线程,每次只能一个线程进入执行
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
else{
break;
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
TicketThread thread1=new TicketThread(); //创建了两个线程对象
TicketThread thread2=new TicketThread();
thread1.start();
thread2.start();
}
}
对于实现Runnable接口的任务类,由于多个线程对应一个任务,只需创建一个该任务类的对象,所以执行的多个线程对应的同步对象只有一个,即所创建的任务类的对象,用this表示
public class TicketTask implements Runnable{
int num=10;
@Override
public void run() {
while(true){
synchronized(this){ //同步对象只有一个,为创建的该类的对象,用this表示
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
if(num==0){
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
TicketTask ticketTask=new TicketTask(); //只创建了一个任务类的对象
Thread thread1=new Thread(ticketTask,"窗口1");
Thread thread2=new Thread(ticketTask,"窗口2");
thread1.start();
thread2.start();
}
}
2>.修饰方法时,不需要手动创建同步对象
锁的对象有两种:
修饰非静态方法时,锁对象默认为this
public class TicketThread implements Runnable{
int num=10;
public void run(){
while(true){
if(num==0){
break;
}
else{
printTicket();
}
}
}
public synchronized void printTicket(){ //synchronized修饰的方法为非静态方法,锁对象为this
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
}
}
public class Test {
public static void main(String[] args) {
TicketThread ticketThread=new TicketThread();
Thread thread1=new Thread(ticketThread,"窗口1");
Thread thread2=new Thread(ticketThread,"窗口2");
thread1.start();
thread2.start();
}
}
修饰静态方法时,锁对象默认为该类的Class对象
//购票案例
public class TicketThread extends Thread{
static int num=10; //共享数据
@Override
public void run() {
while(true){
if(num==0){
break;
}
else{
printTiket();
}
}
}
public static synchronized void printTiket(){ //synchronized修饰静态方法,锁对象是该类的Class对象,Class对象只有一个,实现了多个线程对应一个同步对象
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
}
}
public class Test {
public static void main(String[] args) {
TicketThread thread1=new TicketThread();
thread1.setName("窗口1");
TicketThread thread2=new TicketThread();
thread2.setName("窗口2");
thread1.start();
thread2.start();
}
}