-
线程概述
(1).多线程并发实际上是人类的一种错觉,只是由于cpu运行较快,从而可以频繁的在不同任务之间相互切换,营造了一种多线程同时运行的感觉。
(2).进程和线程:
进程中至少有一个线程,一个线程只能从属于一个进程,而一个进程可以创建多个线程,进程之间资源不共享,但一个进程所创建的多个线程之间共享资源
(3).对于java来说,无论多少个线程,在jvm中只共用一个方法区、一个堆,栈可以有多个。实际上,线程对象.start(),也就是创建分支栈、初始化一些资源,让该线程对象切换到就绪状态,这个过程速度很快。
(4).java多线程调度方式为抢占式,线程对象的生命周期有:新建状态、就绪状态、运行状态、阻塞状态、死亡状态。
(5).java的线程类型分为守护线程和用户线程,main方法就是一个用户线程
线程的几种状态:
-
获取和设置线程对象名字
(1).获取线程对象名字:
String name = 线程对象.getName()
默认名字为Thread-0,Thread-1
(2).修改线程对象名字:
线程对象.setName() -
获取当前线程对象
static Thread currentThread()
静态方法,返回当前线程对象(返回该方法所在的线程对象)
相比较this或者super获取线程对象,他的适用范围更广,可以在非线程对象中获取当前线程对象。 -
线程的sleep方法
static sleep(long millis) throws InterruptedException
Thread类下的一个静态方法
try {
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printstacktracel
}
作用:让当前线程对象进入休眠状态、进入阻塞状态,放弃占有的cpu时间片。让给其他线程使用。
睡眠中的线程的唤醒:线程对象.interuptted(),调用此方法后,sleep方法会抛出异常,从而被捕获退出睡眠
5. 线程终止的正确方式使用布尔标记
public static void main(){
MyThread m = new MyThread();
Thread t = new Thread(m);
m.run = false;
}
public class MyThread implements Runnable{
public bool run = true;
@override
public void run(){
if(run){
}
else{
return;
}
}
}
- yied和join线程让位和线程合并
- 线程不安全的条件和异步同步的概念
一:线程不安全的条件
1.多线程并发2.有共享数据3.共享数据有修改行为
java中三大变量:实例变量、静态变量、局部变量(常量只读,不涉及并发安全问题)其中实例变量和静态变量是共享变量,而局部变量保存在栈中不共享,每一个栈都有属于自己的局部变量,每一个线程都有自己的栈
二:异步就是并发,同步就是线程排队 - synchronized代码块
synchronized("共享对象"){
//代码区
}
当代码执行到synchronized的时候,无论代码块中用不用的到这个对象,程序都会停在synchronized的代码块之前,检查锁池(lockpool)中有没有这个对象的锁,如果有,拿走这个锁,在这一段代码块没有运行完之前,不会释放这个锁,也就是说锁池中(lockpool)中没有这个锁,下一个线程运行到synchronized的时候,在锁池中找不到这个锁,就会停在synchronized代码块之前,直到该锁被释放。从某种程度上说,synchronized关键字实现的功能也可以理解为一种阻塞。
由此,可以理解synchronized关键字中可以填写的内容可以有:
(1).(易理解出错)任意的字符串常量:“123”,“abc”。字符串常量存在于常量池,如果填写字符串常量,所有的线程都会停在synchronized的代码块之前排队
(2).静态变量和实例变量:无论代码块中是否用到这些变量。
- synchronized测试题
public class TEST {
//1.此例在运行中,doOther方法需要等待doSome方法结束吗?不需要
//运行doSome的时候,由于有关键字synchronized修饰,执行到doSome的时候,lockpool中mc的对象锁被拿走,但当t2运行doOther的时候
// ,由于没有synchronized的关键字,根本不会涉及锁池的一些操作,直接执行方法,
// 2.那么将doOther方法修改为synchronized修饰的的时候呢?会等待
//3.创建那么再创建一个Myclass对象mc1,创建t2的时候当做参数传递呢?不会等待,synchronized加在实例方法上,一个对象一把锁
//4.将在3的基础上,将doSome、doOther改为静态方法呢?需要,静态方法上加的锁属于类锁,无论多少对象,锁池中只有一把锁
public static void main(String[] args) {
MyClass mc = new MyClass();
//MyClass mc1 = new MyClass();
Thread t1 = new MyThread(mc);
t1.setName("t1");
Thread t2 = new MyThread(mc);
//Thread t2 = new MyThread(mc1);
t2.setName("t2");
t1.start();
t2.start();
}
}
class MyClass {
public synchronized void doSome(){
System.out.println("doSome begin");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSome over");
}
public void doOther(){
System.out.println("doOther begin");
System.out.println("doOther over");
}
}
class MyThread extends Thread{
public MyClass mc;
public MyThread(MyClass mc) {
this.mc = mc;
}
@Override
public void run() {
if (Thread.currentThread().getName()=="t1") {
mc.doSome();
}
if (Thread.currentThread().getName()=="t2"){
mc.doOther();
}
}
}
11.守护线程、定时器
(1).守护线程(后台线程):一般守护线程是一个死循环,但是当用户线程结束后守护线程也不再守护,自动终止。其中具有代表性的就是jvm的垃圾回收线程。
使用方法:
Thread t = new Thread();
t.setDaemon(true);
t.start();
(2)定时器:使用java.util.Timer
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Test02 {
public static void main(String[] args) throws ParseException {
Timer t = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-HH-mm:ss");
Date d = sdf.parse("2021-01-31-10:19");
t.schedule(new TimeTask1(),d,1000);
// t.schedule(new TimeTask1(),1000,1000);
}
}
class TimeTask1 extends TimerTask{
@Override
public void run() {
System.out.println("start");
}
}
- 实现线程的第三种方式
jdk8新特性:java.util.concurrent.FultureTask
实现步骤:
1.新建类实现callable接口
2.实例化FultureTask对象,构造方法传入实现callable的类
3.通过FultureTask实例对象.get()获取方法返回值,此时主线程阻塞,等待FultureTask线程执行的返回值 - Object的notify和wait方法(易错点)
notify和wait方法是object对象所有,是任何一个java对象都拥有的方法。
o.wait()方法:使正在o对象上作用的线程进入等待状态,无限期等待,并释放o对象的锁
直到调用o.notify()唤醒,但o.notify()并不释放或者加锁 - 死锁的实现代码(知道怎么写才知道怎么避免)
public class lock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Test1(o1,o2);
Thread t2 = new Test2(o1,o2);
t1.start();
t2.start();
}
}
class Test1 extends Thread{
public Object o1;
public Object o2;
public Test1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class Test2 extends Thread{
public Object o1;
public Object o2;
public Test2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
-
一些常识
synchronized的修饰实例方法的时候,一个对象一把锁,加锁的共享对象默认是this,加锁的代码块是整个方法体。
synchronized修饰静态方法:此时为类锁,类锁不管创建了几个对象,都只有一把锁,都得等待
一些常见的线程安全类:StringBuffer、HashTable
非线程安全类:StringBuilder,HashSet、HashMap -
run方法的异常不能throws
原因父类以及接口中的run方法没有抛出异常,原因:子类重写父类方法不能抛出更多和更宽泛的异常