目录
1,join():在那个线程中调用join方法就会网当前线程等待,直到其他线程执行结束。
一,JMM:java内存模型(不是JVM内存区域模型),描述线程的工作内存和主内存的关系。
synchronized特性:1,互斥性 2,强制刷新内存 3,可重入。
八,线程的等待与唤醒机制(wait(),notify()),但是必须搭配synchronized锁来执行。
懒汉式单例:在需要这个对象时,才会产生对象,此时的线程时不安全的
饿汉式单例:在类的初始化的时候创建对象,在类初始化的时候,就会创建对象,线程安全
定时器:Timer类,设定一个时间,以及一个相应的任务,在指定时间后执行某个任务。
一,线程,进程,程序。
一,概念
程序:一系列有组织的文件,操作系统实现封装API,实现不同的效果
进程:程序的一次执行过程,进程是操作系统资源分配的最小单位。
线程:进程中的子任务(QQ和两个不同的聊天就是QQ进程中的两个子线程)
一个进程至少包含一个主线程,存在多个不同的子线程,多个线程共享操作系统分配给进程的资源,线程是操作系统调度的最小单位。
如何定位主机上的一个进程:
不同的程序拥有不同的端口号,操作系统通过不同的端口号,来定位进程,给进程分配资源。进程的端口号是不会变化的,但是进程的PCB(进程的信息)是变化的。
二,进程与线程区别:
1,进程是OS资源分配的基本单位,线程是OS调度的基本单位。
2,创建和销毁一个进程,远比创建和销毁一个线程慢的多,线程是轻量级的。
3,调度一个线程也比调度一个进程快得多。
4,一个进程至少包含一个主线程,可以包含多个线程
5,不同进程之间相互独立,不会共享资源,同一个进程的线程之间的共享进程的资源。
三,创建线程的四种方式
一,继承Thread类,覆写run方法
继承Thread类是创建一个线程对象。
public class ExtendThread extends Thread{
@Override
public void run() {
System.out.println("子线程");
System.out.println("子线程");
System.out.println("子线程");
System.out.println("子线程");
}
}
class MyThread {
public static void main(String[] args) {
ExtendThread t1 = new ExtendThread();
t1.start();
System.out.println("主线程的方法");
}
}
二,实现Runable接口,覆写run方法。
实现Runnable接口是创建一个子线程的任务。实现Runnable接口更加灵活,还可以继承其他类和实现其他接口,使用继承就不能狗继承其他类。
public class ImplRunable implements Runnable{
@Override
public void run() {
System.out.println("子线程");
}
}
class Main{
public static void main(String[] args) {
Thread thread = new Thread(new ImplRunable());
thread.start();
System.out.println("主线程.....");
}
}
1,继承Thread类和实现Runnable接口的区别:
区别:继承Thread类直接产生的线程对象,而实现这个接口只是创建了一个线程任务,在需要启动这个线程任务时,直接将线程任务对象传给Thread类,就可以将启动该任务。
共同:最终线程启动都是调用线程start()方法,才能启动。
2,lambda表达式实现创建线程写法
public class ThreadBy {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("线程1");
},"线程1");
thread.start();
System.out.println("主线程");
}
}
3,匿名内部类实现写法
Thread thread1 = new Thread(){
@Override
public void run() {
System.out.println("匿名内部类");
}
};
thread1.start();
4,Runnable与匿名内部类创建线程写法
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable与匿名内部类实现创建线程");
}
},"匿名,Runnbale");
thread2.start()
四,实现Callable接口,覆写call方法
五,使用线程池
多线程进阶上,线程池及锁的介绍http://t.csdn.cn/BS7dd
六,多线程与顺序执行的速度差
public class MoreThreadSpeed {
static long x = 0;
static long y = 0;
public static void main(String[] args) throws InterruptedException {
fun();
fun1();
}
public static void fun() throws InterruptedException {
Thread t1 = new Thread(() ->{
for (int i = 0; i < 5000_0000_0; i++) {
x++;
}
});
for (int i = 0; i < 5000_0000_0; i++) {
x++;
}
long start = System.currentTimeMillis();
t1.start();
t1.join();
long end = System.currentTimeMillis();
System.out.println(end - start + " " +x);
}
public static void fun1(){
long start = System.currentTimeMillis();
for (int i = 0;i < 1000_0000_00;i++){
y++;
}
long end = System.currentTimeMillis();
System.out.println(end - start + " " +y);
}
}
四,线程的属性
1,ID 获取ID的方法:getID(); // ID是线程的唯一表示PID,不同线程的ID不会重复
2,名称 获取方法:getName(); // 名称各种调试工具用到
3,状态 获取方法 : getState(); // 线程处在运行态还是阻塞态 等等。
4,优先级 获取方法: getPriority(); //优先级越高,越先被调度,最低是1,最高为10,普通线程一般为5。
5,是否后台线程 获取方法: isDaemon(); // 后台线程,jvm会在最后一个后台线程结束以后关闭
6,是否存活 获取方法:isAlive(); // run方法是否运行结束
7,是否被中断 获取方法:isInterrupted(); // 线程是否被中断运行
public class ThreadAttr {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() ->{
for (int i = 0; i < 2; i++) {
System.out.println("线程ID" + Thread.currentThread().getId());
System.out.println("线程名" +Thread.currentThread().getName());
System.out.println("线程优先级"+Thread.currentThread().getPriority());
System.out.println("线程状体"+Thread.currentThread().getState());
System.out.println("后台线程 ? "+Thread.currentThread().isDaemon());
System.out.println("存活 ?"+Thread.currentThread().isAlive());
System.out.println("被中断 ?" +Thread.currentThread().isInterrupted());
try {
Thread.sleep(1000);
System.out.println("___________________________________");
System.out.println("睡眠线程状态"+Thread.currentThread().getState());
System.out.println("睡眠和后台线程 ? "+Thread.currentThread().isDaemon());
System.out.println("睡眠和存活 ?"+Thread.currentThread().isAlive());
System.out.println("睡眠后被中断 ?" +Thread.currentThread().isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
thread.join();
System.out.println("___________________________________");
System.out.println("线程ID" + Thread.currentThread().getId());
System.out.println("线程名" +Thread.currentThread().getName());
System.out.println("线程优先级"+Thread.currentThread().getPriority());
System.out.println("线程状体"+Thread.currentThread().getState());
System.out.println("后台线程 ? "+Thread.currentThread().isDaemon());
System.out.println("存活 ?"+Thread.currentThread().isAlive());
System.out.println("被中断 ?" +Thread.currentThread().isInterrupted());
}
}
五,线程的常用方法
Thread类的方法,在那个线程中调用,就在那个线程生效。
一,run()方法
package 多线程.复习;
import java.util.Random;
public class ThreadDemo1 {
private static class MyThread extends Thread{
@Override
public void run() {
Random random = new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
Random random = new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程类的run方法:run方法是线程的核心方法,,在java.lang.Thread包中,线程中所有要做的事都在run方法中。
启动线程调用start()方法,线程启动以后,JVM会自动调用各个线程的run方法,多个线程之间是并行运行。
二,start()方法,线程的启动方法。
JDK与JRE的区别:
1,JRE:java运行时环境,面向java程序的使用者,在一台计算机上安装了jre,就可以运行java程序,jre包括了JVM和java核心类库。
2,JDK:面向开发人员,JDK中包括了,两个jre,java核心类库,java开发工具和开发工具包,jre中在JDK下的JRE目录时Java运行环境的jre,另一个时java开发环境下的jre。
jconsle:查看当前JVM内部的线程情况。有JDK提供的开发工具。
start()方法和run()方法区别:
start()方法:启动线程,将线程从创建态改变为就绪态,当时间片轮到该线程时,jvm就会自动调用线程run方法,进入运行态,在run方法执行结束以后就会进入终止态。多个线程启动start()方法,多个线程并发执行。
run():线程的普通方法,如果一个线程没有启动,而直接调用run方法,那么执行的就不是该线程,而是主线程,不调用start方法,而直接调用run方法,多个线程会顺序执行,并不能达到线程并发的效果,
如果在线程1还没有执行结束的时候,再次调用线程1的start方法,就回报线程状态异常的错误。
三,线程中断方法——线程之间通信
中断线程:在线程还在运行是,剥夺线程的资源,就会中断线程。
1,通过共享变量进行中断
public class ThreadInterruptrd {
private static class MyThread implements Runnable{
boolean isRupt = false;//通过共享这一个变量,来控制线程的中断
@Override
public void run() {
while (!isRupt){
System.out.println("正在通话中。。。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("抢线了,糟糕,电话挂掉了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
Thread thread = new Thread(t1,"子线程");
thread.start();
System.out.println("主线程信号良好。。。");
Thread.sleep(2000);
t1.isRupt = true;
System.out.println("主线程信号太差了。。。");
}
}
2,通过 thread.interrupt(); 中断线程
//通过内置的属性中断线程
public class ThreadInterrupted {
private static class MyRunnable implements Runnable{
@Override
public void run() {
while (true){
System.out.println("渣渣正在通话中。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.err.println("那个狗日剪我电话线");
break;
}
}
System.out.println("渣渣被迫挂了电话。。。。");
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable,"渣渣");
thread.start();
System.out.println("主线程安全");
Thread.sleep(2000);
// System.out.println("警报!!!有人在剪电话线。。");
thread.interrupt();
System.out.println("电话线断了。。。");
}
}
线程内置中断的两种方法
a,当线程调用sleep/wait/join方法时等方法处在阻塞状态,收到中断通知thread.isinterruptrd,就会抛出一个InterruptedException异常,当抛出后,当前线程的中断状态就会被清除。
也就是说当中断异常产生以后,会向外抛出异常,线程的状态会从中断状态还原成运行状态。
b,判断线程是否被中断
interrupted()(就像一个会回弹的开关),isInterRupted()方法(就像一个不会回弹的开关)。
interrupted线程的静态方法,判断线程是否被中断:Thread.interrupted(),清楚中断标志
isInterrupted线程的实例方法,判断线程是否被中断。thread.isInterrupted()不会清楚中断标志。
三,线程的等待方法;
1,join():在那个线程中调用join方法就会网当前线程等待,直到其他线程执行结束。
在主方法中调用子线程的join,阻塞的是主线程,直到子线程执行结束,才会执行后面的代码。
public class JoinOfThread {
private static class JoinMethod implements Runnable{
int num = 0;
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("子线程占用资源");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new JoinMethod(),"子线程");
thread.start();
thread.join();//在主线程中调用子线程的join方法,就会让主线程进入阻塞状态,直到子线程执行结束,才会开始主线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程占用资源");
Thread.sleep(1000);
}
}
}
2,wait();
3,sleep();
4,yield()
5,notify() & wait()
package 多线程.复习.线程方法;
import 多线程.复习.线程安全.SynchronizedKey;
public class NotifyAndWait {
private static class ThreadOfOB{
static Object lock = new Object();
}
public static void main(String[] args) {
ThreadOfOB ob = new ThreadOfOB();
Thread t1 = new Thread(()->{
synchronized(ob.lock) {
try {
System.out.println(Thread.currentThread().getName()+"线程进入等待状态");
ob.lock.wait();
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"线程被唤醒");
ob.lock.notify();
}
});
Thread t2 = new Thread(() ->{
synchronized(ob.lock) {
try {
System.out.println(Thread.currentThread().getName()+"线程进入等待状态");
ob.lock.wait();
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"线程被唤醒");
}
});
Thread t3 = new Thread(() ->{
synchronized(ob.lock) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"准备唤醒锁");
ob.lock.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"唤醒了lock锁");
}
});
t1.start();
t2.start();
t3.start();
}
}
六,线程的状态
public class StateThread {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
一,线程新建运行和终止状态。
isAlive:除了 new 和 terminated 都是存活状态
二,三种阻塞状态
public class WaitState {
public static void main(String[] args) {
Object lock = new Object();
Thread t1 = new Thread(() ->{
synchronized (lock){
while (true){
try {
// Thread.sleep(1000);//调用此方法,线程1处在超时等待状态:timed-waiting
lock.wait();//调用wait,线程1进入wait状态,等待其他线程唤醒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
},"t1");
Thread t2 = new Thread(() ->{
synchronized (lock){
System.out.println("她");
}
},"t2");
t1.start();
t2.start();
}
}
waiting:通过Object类的wait方法,将线程置入等待状态,需要通过OBject类的notify唤醒
blocked:锁等待,需要获取锁,才能进入运行态
timed-wating:超时等待状态。超时自动唤醒线程
七,线程安全
线程串行执行和并行执行结果一致,那么就是线程不安全的。
static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() ->{
for (int i = 0; i < 5000; i++) {
count++;
}
});
Thread t2 = new Thread(() ->{
for (int i = 0; i < 5000; i++) {
count++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
一,JMM:java内存模型(不是JVM内存区域模型),描述线程的工作内存和主内存的关系。
描述java在多线程的场景下,java的线程内存和主内存的关系。
每个线程在工作时,都会从主内存中读取共享变量(不是线程的内部变量),关于共享变量的操作都是在工作内存中,工作内存执行完毕后,写回主内存。
二,线程安全必须同时保证以下三个特性
1,原子性
该操作不会被中断,要么全部执行,要不都不执行,不存在中间操作。
例如:a = 10 为原子操作,将10直接赋给a;
a ++ 为非原子操作;读a,运算a+1, a = a +1;分三步走。
2,可见性
一个线程堆一个共享变量的修改,其他线程可见,这种特性为可见性。
3,防止指令重排
cpu对指令执行的顺序进行调整。
要保证安全,就必须保证原子性,可见性和防止指令重排
三,synchronized(同步)关键字——对象锁
synchronized保证原子性和可见性。
锁:在java内部每个对象都有一块内存(临界区),描述当前对象 "锁"的信息,锁的信息就表示当前对象被那个线程所持有。
若锁信息没有保存线程,就表示当前对象被那个线程锁持有
若锁信息保存了线程id,其他线程要获取锁,就会处在阻塞状态。(等待队列不是FIFO的算法)
synchronized特性:1,互斥性 2,强制刷新内存 3,可重入。
互斥性:多个线程访问同一个资源,一次只允许一个线程对对资源进行加锁,其他线程进入阻塞状态。
强制刷新内存:可见性和原子性保证了,在读写操作未完成之前,临界区资源只能被当前线程访问,当前线程读写完毕,会强制刷新主存临界区资源的值。
可重入:获取到临界的资源以后,可以再次对临界区内的资源进行加锁。
private static class Count{
int count = 0;
synchronized void increased(){
count++;
}
synchronized void increased1(){
//当进入increased1方法,需要再次进入increased方法,如果不支持可重入,就会线程就会一致再次等待,就会死锁
increased();
}
}
synchronized是个对象锁,必须要有集体的对象让他锁:
1,synchronized修饰成员方法,则锁的就是调用该方法的对象。
synchronized void increased2(){
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
Count c1 = new Count();
Count c2 = new Count();
Count c3 = new Count();
Thread t1 = new Thread(() -> {
c1.increased2();
},"t1");
Thread t2 = new Thread(() -> {
c2.increased2();
},"t2");
Thread t3 = new Thread(() -> {
c3.increased2();
},"t3");
t1.start();
t2.start();
t3.start();
}
修饰成员方法,只会锁当前对象。三个线程不是同一个对象调用的,因此三个线程轮流访问该成员方法。
2,synchronized修饰静态方法,则锁的就是全局为一的通过反射获得到的类名.class对象
synchronized static void increased2(){
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
Count c1 = new Count();
Count c2 = new Count();
Count c3 = new Count();
Thread t1 = new Thread(() -> {
c1.increased2();
},"t1");
Thread t2 = new Thread(() -> {
c2.increased2();
},"t2");
Thread t3 = new Thread(() -> {
c3.increased2();
},"t3");
t1.start();
t2.start();
t3.start();
}
修饰静态方法,锁的是全局唯一的class对象,因此,线程1最先获取到该对象,因此线程1一直持有锁,才会一直打印线程1。无论通过那个count对象,只有一个对象能方法。
Java的并发工具包:java.util.concurrent
volatile 关键字:可见性,内存屏障
使用volatile关键字保证可见性,不能保证原子性
1,线程每次访问volatile关键字,无论该线程中是否已经读取该变量,都会到主内存中再次读取该变量。
public class NonVolatile {
private static class Count{
int count = 0;
}
public static void main(String[] args) {
Count count = new Count();
Thread t1 = new Thread(() -> {
while (count.count == 0){
}
System.out.println(count.count + ":退出循环");
},"t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000);
// count.count++;
Scanner sc = new Scanner(System.in);
System.out.println("count = ");
count.count = sc.nextInt();
System.out.println("t2的JJM中的count值为:"+count.count);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"t2");
t2.start();
}
}
2,线程写volatile关键字时,该线程立即将写完之后的变量强制刷新到内存,其他线程访问会一直等待,知道该线程写操作结束。
使用volatile关键字修饰变量,相当于一个内存屏障
八,线程的等待与唤醒机制(wait(),notify()),但是必须搭配synchronized锁来执行。
九,单例模式
单例模式:保证某个类在程序中,有且只有一个对象。
单例模式的设计:
懒汉式单例:在需要这个对象时,才会产生对象,此时的线程时不安全的
1,私有化构造方法,
public class 单例模式 {
private 单例模式(){}//私有化构造方法。
}
2,在本类中自己创建私有的静态的一个对象(设置为静态的原因是因为,成员对象只能通过对象来调用,而构造方法在外部不可见,无法创建对象来获取该私有对象这个属性,因此采用静态的修饰,在类加载的时候创建对象。)
public class 单例模式 {
private static 单例模式 single;
private 单例模式(){}//私有化构造方法。
}
3,类加载阶段创建着唯一的对象。
public class 单例模式 {//懒汉
private static 单例模式 single;
private 单例模式(){}//私有化构造方法。
public static 单例模式 getObject(){
single = new 单例模式();//在需要这个对象才创建对象
return single;//通过这个方法来返回这个唯一的对象
}
}
饿汉式单例:在类的初始化的时候创建对象,在类初始化的时候,就会创建对象,线程安全
1,私有化构造方法和创建属性
public class 单例模式 {
private static 单例模式 single = new 单例模式();
private 单例模式(){}//私有化构造方法。
}
2,并返回对象。
public class 单例模式 {
private static 单例模式 single = new 单例模式();
private 单例模式(){}//私有化构造方法。
public static 单例模式 getObject(){
return single;//通过这个方法来返回这个唯一的对象
}
}
解决懒汉式单例模式的线程安全问题
直接对方法加synchronized,这种方法锁的粒度太粗,如果存在其他的加锁方法,就不会并发。
class LizeTon{
private static LizeTon lizeTon ;
private LizeTon(){}
//直接在获取对象的方法上加synchronized关键字,来保证原子性和可见性。
private static synchronized LizeTon getLizeTon(){
if (lizeTon == null){
lizeTon = new LizeTon();
}
return lizeTon;
}
}
在方法里面加锁
class LizeTon{
private static LizeTon lizeTon ;
private LizeTon(){}
//直接在获取对象的方法上加synchronized关键字,来保证原子性和可见性。
private static LizeTon getLizeTon(){
if (lizeTon == null){//多个线程可以同时进入该方法,
synchronized (LizeTon.class){//在这里会阻塞多个线程
if(lizeTon == null){//一个线程执行结束后,其他线程进来再次读lizeTon,保证不会重复创建对象
lizeTon = new LizeTon();
}
}
}
return lizeTon;
}
}
双重枷锁:
使用volatile关键字,保证实例化不会中断。禁止指令重排
JDK中的阻塞队列
BlockingQueue:put入,take出
生产消费者模型:
import java.util.Random;
import java.util.concurrent.*;
public class 生产消费者 {
public static void main(String[] args) {
BlockingQueue blockingDeque = new LinkedBlockingQueue(2);
Thread t1 = new Thread(() -> {
while (true){
try {
int val = (int) blockingDeque.take();
System.out.println("消费者消费:" + val);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"消费者");
Random random = new Random();
Thread t2 =new Thread(() -> {
while (true){
try {
int val = random.nextInt(100);
System.out.println("生产者生产:" + val);
blockingDeque.put(val);//在次设置为墙,防止指令重排
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"生产者");
t1.start();
t2.start();
}
}
定时器:Timer类,设定一个时间,以及一个相应的任务,在指定时间后执行某个任务。
核心方法:
schedule方法:
public class 定时器Timer {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3S后打印");
}
},3000);
}
}