1.线程中常用的方法
1.static Thread currentThread()---得到当前正在运行的对象
2.void start---启动线程
3.String getName()---返回该线程的名称
1.当线程没有设置名称时,系统会赋予一个默认的线程名称“Thread-0,Thread-1......”
2.主线程【主方法的执行线程】的默认名称是"main"
4.void setName()---设置线程名称
例如:
package com.object.test1;
public class MethodClass implements Runnable{
@Override
public void run() {
for(int i=1;i<=50;i++){
//1.static Thread currentThread()得到当前正在运行的线程对象
Thread th=Thread.currentThread();
//3.String getName()返回该线程的名称。
String name=th.getName();
System.out.println(name+"--i--"+i);
}
}
}
package com.object.test1;
public class MyMain {
public static void main(String[] args) {
//2.主线程【主方法的执行线程】的名称默认是“main”
String name=Thread.currentThread().getName();
System.out.println(name);
//创建目标对象
MethodClass my1=new MethodClass();
MethodClass my2=new MethodClass();
//创建线程对象
Thread th1=new Thread(my1);
Thread th2=new Thread(my2);
//1.当没有设置线程名称的时候,系统会赋予线程一个默认的名称“Thread-0,Thread-1......”
/*//4.void setName(String name)设置线程名称
th1.setName("线程");
th2.setName("窗口");
*/
//2.void start() 启动线程
th1.start();
th2.start();
}
}
线程的优先级----就是指线程的执行先后,默认情况下所有线程的优先级都是一样的,都是5。
我们可以通过:
1.void etPriority(int newPriority)---更改线程优先级
1.线程的优先级有10个级别,分别是1~10来表示。数字越大优先级越高
2.为了方便操作,java将10个级别规定成三个级别,分别是最低优先级、中等优先级、
最高优先级,并且将这3个级别封装成静态常量
static int MAX_PRIORITY---最高的优先级【10】
static int MIN_PRIORITY---最低优先级【1】
static int NORM-PRIORITY---中等优先级【5】
2.int getPriority()---返回线程的优先级
3.设置现成的优先级时,数字越大优先级越高,数字越小优先级越低。优先级高并不会代表一定就会有限执行,知识被优先执行的几率增大,因此不要试图通过控制线程的优先级来保证某一个线程,总是第一个执行
例如:
package com.object.test2;
public class MethodClass implements Runnable{
@Override
public void run() {
for(int i=1;i<=50;i++){
//1.static Thread currentThread()得到当前正在运行的线程对象
Thread th=Thread.currentThread();
//3.String getName()返回该线程的名称。
String name=th.getName();
System.out.println(name+"--i--"+i);
}
}
}
package com.object.test2;
public class MyMain {
public static void main(String[] args) {
//2.主线程【主方法的执行线程】的名称默认是“main”
String name=Thread.currentThread().getName();
System.out.println(name);
int mainpri=Thread.currentThread().getPriority();
System.out.println("主方法执行线程的名称=="+name+",优先级=="+mainpri);
//创建目标对象
MethodClass my1=new MethodClass();
//创建线程对象
Thread th1=new Thread(my1);
Thread th2=new Thread(my1);
//4.void setName(String name)设置线程名称
th1.setName("线程");
th2.setName("窗口");
//void setPriority(int newPriority) 更改线程的优先级。
//1.线程的优先级有10个级别,分别使用整数1~10来表示。数字越大优先级越高。
th1.setPriority(5);
/*
2.为了方便操作,java将10个级别有规定成3个级别,分别是最低的优先级,中等优先级,最高的优先级,并且将这3个级别封装成了静态常量 static int MAX_PRIORITY 线程可以具有的最高优先级。10
static int MIN_PRIORITY线程可以具有的最低优先级。1
static int NORM_PRIORITY分配给线程的默认优先级。5
*/
th2.setPriority(Thread.MAX_PRIORITY);
//int getPriority() 返回线程的优先级。
int in1=th1.getPriority();
int in2=th2.getPriority();
System.out.println("线程1优先级"+in1);
System.out.println("线程2优先级"+in2);
th1.start();
th2.start();
}
}
2.守护线程的相关操作
用户线程---通常情况下我们所创建的是普通县城,非守护线程,也是守护线程
守护线程---也叫精灵线程,当所有用户线程执行完毕后,自动结束运行的线程就是守护线程
1.boolean isDaemom()---测试线程是否为守护线程
2.void setDaemom()---将该线程标记为守护线程
特征:当所有用户线程都执行完毕以后,无论守护线程能否可以继续运行,都要立刻停止运行。
package com.object.test3;
public class MethodClass implements Runnable{
@Override
public void run() {
for(int i=1;i<=50;i++){
//1.static Thread currentThread()得到当前正在运行的线程对象
Thread th=Thread.currentThread();
//3.String getName()返回该线程的名称。
String name=th.getName();
System.out.println(name+"--i--"+i);
}
}
}
package com.object.test3;
public class MyThread implements Runnable{
@Override
public void run() {
while(true){
Thread th=Thread.currentThread();
String name=th.getName();
System.out.println(name);
}
}
}
package com.object.test3;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MethodClass my1=new MethodClass();
MyThread my2=new MyThread();
//创建线程对象,并且命名
Thread th1=new Thread(my1,"用户线程1");
Thread th2=new Thread(my1,"用户线程11");
Thread th3=new Thread(my2,"守护线程");
//void setDaemon(boolean on) 将该线程标记为守护线程用户线程。
th3.setDaemon(true);
//boolean isDaemon() 测试该线程是否为守护线程。
System.out.println("用户1--"+th1.isDaemon());
System.out.println("用户11--"+th2.isDaemon());
System.out.println("守护1--"+th3.isDaemon());
//启动线程
th1.start();
th2.start();
th3.start();
}
}
1.void sleep(long millis)---设置线程休眠【暂停】指定时间【毫秒】
例如:
package com.object.test4;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.InflaterInputStream;
public class MyAlarm {
public static void main(String[] args) throws Exception{
System.out.println("设置一个闹钟:");
BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
String nz=input.readLine();
SimpleDateFormat smd=new SimpleDateFormat("HH:mm:ss");
boolean flag=true;
while(flag){
String dq=smd.format(new Date());
if(dq.equals(nz)){
flag=false;
}
System.out.println(dq);
Thread.sleep(1000);
}
System.out.println("闹钟响起");
}
}
2.void interrupt()---中断线程休眠【粘贴】会进入异常
例如:
package com.object.test3;
public class MyMain {
public static void main(String[] args) {
System.out.println("上课铃声响了");
//创建目标对象
MyThreadClass my1=new MyThreadClass();
//创建线程对象
Thread th=new Thread(my1);
//修改名称
th.setName("张三");
//启动线程
th.start();
System.out.println("老师开始上课");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老师发现同学睡着啦");
System.out.println("老师走过去,踹了一脚!");
//void interrupt() 中断线程休眠【暂停】。会进入异常【InterruptedException】。
th.interrupt();
}
}
3.void join(long millis)---【强制线程执行】等待该线程终止的时间最长为millis毫秒
例如:
package com.object.test3;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MethodClass my1=new MethodClass();
MyThread my2=new MyThread();
//创建线程对象,并且命名
Thread th1=new Thread(my1,"用户线程1");
Thread th2=new Thread(my1,"用户线程11");
Thread th3=new Thread(my2,"守护线程");
//void setDaemon(boolean on) 将该线程标记为守护线程用户线程。
th3.setDaemon(true);
//boolean isDaemon() 测试该线程是否为守护线程。
System.out.println("用户1--"+th1.isDaemon());
System.out.println("用户11--"+th2.isDaemon());
System.out.println("守护1--"+th3.isDaemon());
//启动线程
th1.start();
th2.start();
th3.start();
}
}
3.线程的生命周期
1.线程生命周期就是线程从一开始新建,到run方法执行完毕以后的状态变化.[状态之间的切换]
2.线城生命周期的几种状态【1.新建状态;2.就绪状态;3.运行状态;4.阻塞状态;5.死亡状态】
3.线程的生命周期描述
1.新建状态
通过new的方式创建出线程对象,此时线程就进入到创建状态【新建状态】
新建状态的线程是不能运行的
新建状态的线程调用start方法,进入就绪状态
2.就绪状态
线程具备运行能力,只差操作系统[CPU]费赔给运行时间片
得到操作系统[CPU]给的运行片时,开始执行run方法,进入运行状态
3.运行状态:线程运行run方法
回到就绪状态:
1.操作系统[CPU]分配给运行时间使用完毕后,就会回到就绪状态
进入阻塞状态:
1.运行状态执行sleep方法,进入阻塞状态
2.运行状态执行了wait方法,进入阻塞状态。
3.运行状态执行了输入/输出动作。进入阻塞状态
4.阻塞状态:线程运行暂停
回到运行状态
阻塞状态中的县城,结束了造成阻塞的原因,此时线程进入就绪状态,得到操作系统[CPU]配 给他的运行状态就可以进入运行状态
运行状态进入阻塞状态原因:
1.运行状态执行sleep方法,进入阻塞状态,结束休眠时间/interrupt,进入运行状态
2.运行状态执行wait方法,进入阻塞状态,调用notify/notifyAll,进入就绪状态
3.运行状态执行输入输出动作,进入阻塞状态,输入/输出结束后就可以进入就绪状态
5.死亡状态:线程运行结束,释放运行资源
死亡状态的线程是不能运行的,除非再一次使用start方法重新启动运行。
4.线程安全
1. 买票实例:
package com.object.test6;
public class MyThreadClass implements Runnable{
private int ticket=5;
public void run() {
//得到线程名称
String name=Thread.currentThread().getName();
//持续买票
boolean flag=true;
//只要有票就一直卖
while(flag){
//判断有没有票卖
if(ticket>0){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name+",卖出1张票,还剩"+(--ticket)+"张");
}else{
flag=false;
}
}
}
}
package com.object.test6;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MyThreadClass my=new MyThreadClass();
//创建线程对象
Thread th=new Thread(my);
Thread th2=new Thread(my);
Thread th3=new Thread(my);
//修改线程名称
th.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//启动线程
th.start();
th2.start();
th3.start();
}
}
2.分析结果:
当窗口3卖出最后一张票时,在收钱打印的时候,还没来得及进行减1线程就切换到了窗口2,这是窗口2就认为还有一张票,然后就在窗口2进行了收钱打印时,还没来得及对票数减1县城就切换到窗口3,这时窗口3就会对票数进行减1,完成以后切换给窗口2,窗口2再进行减1,此时就导致了出现-1的这个值
经过上面运行程序的分析,我得到的结果是:
当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。
为了解决这种数据不一致的错误情况,我们才学习线程同步。
3.什么是线程同步/线程安全
线程同步也叫线程安全,当多条线程同时访问一个公共资源时,每一次只能由一条访问公共资源,当这一条线程访问公共资源时其他的都处于等待状态,不可以进行对公共资源访问。当这条线程访问完毕后,其他线程中的一条线程才能访问资源,剩下的资源继续等待当前线程访问结束,实现的这个过程就是线程访问。【排队访问资源】
线程同步[线程安全]的实现方式:
1. Synchronized关键字【同步代码块/同步方法】
1.同步代码块
格式:synchronized(同步对象){ }
缺点:使用时需要设置同步对象,由于很多时候不知道同步对象是谁,容易写错,导致思索的情况,正是因为这也原因很少使用同步代码块来实现线程同步。
例如:
package com.object.test6;
public class MyThreadClass2 implements Runnable {
private int ticket = 5;
public void run() {
// 得到线程名称
String name = Thread.currentThread().getName();
// 持续买票
boolean flag = true;
// 只要有票就一直卖
while (flag) {
synchronized (this) {
// 判断有没有票卖
if (ticket > 0) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name + ",卖出1张票,还剩" + (--ticket) + "张");
} else {
flag = false;
}
}
}
}
}
package com.object.test6;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MyThreadClass2 my=new MyThreadClass2();
//创建线程对象
Thread th=new Thread(my);
Thread th2=new Thread(my);
Thread th3=new Thread(my);
//修改线程名称
th.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//启动线程
th.start();
th2.start();
th3.start();
}
}
2.同步方法
同步方法的定义格式:
访问限制修饰符 synchronized 访问返回值类型 方法mingcheng(){ }
例如:
package com.object.test6;
public class MyThreadClass3 implements Runnable {
private int ticket = 5;
// 持续买票
boolean flag = true;
public void run() {
// 得到线程名称
String name = Thread.currentThread().getName();
// 只要有票就一直卖
while (flag) {
sell(name);
}
}
//同步方法
public synchronized void sell(String name){
// 判断有没有票卖
if (ticket > 0) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name + ",卖出1张票,还剩" + (--ticket) + "张");
} else {
flag = false;
}
}
}
package com.object.test6;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MyThreadClass3 my=new MyThreadClass3();
//创建线程对象
Thread th=new Thread(my);
Thread th2=new Thread(my);
Thread th3=new Thread(my);
//修改线程名称
th.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//启动线程
th.start();
th2.start();
th3.start();
}
}
3.通过Lock接口
1. 格式:
public interface Lock
2.常用的接口方法
void lock() 获得锁。
void unlock() 释放锁。
由于上面的锁方法是Lock接口,我们要使用就得先创建出Lock接口对象,由于Lock是个接口不能new ,我们就得使用它的子类来创建对象。Lock接口得子类ReentrantLock
例如:
package com.object.test6;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThreadClass4 implements Runnable {
private int ticket = 5;
private Lock mylock=new ReentrantLock();
public void run() {
// 得到线程名称
String name = Thread.currentThread().getName();
// 持续买票
boolean flag = true;
// 只要有票就一直卖
while (flag) {
//void lock() 获得锁。
mylock.lock();
// 判断有没有票卖
if (ticket > 0) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name + ",卖出1张票,还剩" + (--ticket) + "张");
} else {
flag = false;
}
//void unlock() 释放锁。
mylock.unlock();
}
}
}
package com.object.test6;
public class MyMain {
public static void main(String[] args) {
//创建目标对象
MyThreadClass4 my=new MyThreadClass4();
//创建线程对象
Thread th=new Thread(my);
Thread th2=new Thread(my);
Thread th3=new Thread(my);
//修改线程名称
th.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//启动线程
th.start();
th2.start();
th3.start();
}
}
5.Synchronized关键字与Lock接口的区别?
synchronized | Lock |
synchronized关键字 | Lock接口 |
自动锁定资源,不灵活 | 手动锁定资源,灵活 |
异常时会自动释放锁 | 异常时不会自动释放锁,所以需要在finally中实现释放锁 |
不能中断锁,必须等待线程执行完成释放锁 | 可以中断锁 |