进程: 正在执行的程序,进程依赖于线程运行。
线程: 进程中的一个控制流程单元
一个进程中至少有一个控制单元(控制流程),仅有一个控制单元称为单线程程序。
多线程: 一个进程中当有多个控制单元时候,就称为多线程程序。
开启多线程的好处:
1. 提高效率,原理就是合理使用CPU资源。
2. 可以让多部分代码同时执行。
*****************************************************************
线程的特性:随机性。原理:CPU不断的进行时间片的切换。
想要使用多线程技术,java对线程这类事物进行描述,提供了一个对象:Thread.
明确一点:只有Thread类或者Thread的子类对象才可以调用系统资源创建控制单元。
创建线程的两种方式:
1. 继承Thread类
*继承Thread
*复写run方法,将多线程需要运行的代码存入该方法中。
*创建Thread的子类对象(创建线程)
*调用start方法开启线程。(start方法做了两个动作,开启线程,调用run方法)
2. 实现Runnable接口
*实现Runnable接口
*复写接口中的run方法
*创建Runnable接口的子类对象
*创建Thread类的对象(创建线程)
*将Runnable接口的子类对象作为参数传递给Thread类的构造函数。因为要让多线程去执行指定的run方法
*调用Thread类的start方法开启线程。
两种方式的区别:
1. 实现方式和继承方式使用的run方法的位置不同
2. 实现方式避免了单继承的局限性***
线程的生命周期:
*被创建: new
*可运行: start
*冻结: 线程还活着,但是CPU不执行它 sleep,wait
*销毁: 线程挂掉(线程执行代码结束,线程就停止)
线程安全问题:
引发原因:当线程运行的代码中有对成员变量(共享数据)进行多次操作时,就有可能引发安全问题,因为多条操作同一资源的语句被多个线程分开运行就会发生。
解决原理:在同一时刻,只让一个线程将这些操作同一资源的代码执行完即可。在这个过程中,其他线程不能执行该代码段。Java提供了同步方式来完成线程安全问题的解决。
同步的原理:通过带锁的代码块将容易出现安全问题的代码进行封装。
同步的好处:结局了线程安全问题。
同步的弊端:降低了运行效率。判断锁比较消耗资源。
同步的表现形式:
1. 同步代码块
synchronzied(对象锁){
需要被同步的代码(哪些需要同步哪些不需要一定要分清)
}
2. 同步函数
就是在函数上加了synchronzied关键字进行修饰
同步代码块可以使用任意对象作为锁,同步函数使用的锁只有一个,就是this
同步的前提:
1. 必须是两个或者两个以上线程
2. 多个线程必须使用同一个锁
*****************************************************************
每一个线程都有自己默认的名称:Thread-编号,该编号从0开始
Thread currentThread():获取线程对象的引用
String getName():获取线程的名称
主函数所在的线程名称为:main
*****************************************************************
线程的状态:
同步的形式:1.同步代码块
2.同步函数
**同步函数使用的是什么锁?因为函数都需要被对象调用,所以函数都有一个对象所属。该对象使用this来表示,所以同步函数使用的锁是this。可以通过两个线程来验证,一个线程在同步语句块中,一个在同步函数中。如果使用的不是同一个锁就会出现安全问题。
Static是随着类的加载而加载,这时还没有建立该类的对象。只有类被加载进了内存,这时只有一个对象存在,就是该类所对应的字节码文件对象。
静态同步方法所使用的锁时:该方法所属类的字节码文件对象。即:本类名.class
单例设计模式两种:先加载,延迟加载。延迟加载懒汉式,有线程安全隐患,因为有多个线程在同时操作资源,要加上同步,这个同步用的锁是本类的字节码对象。
/**
* 懒汉式:当多个线程并发访问该方法时,有可能出现线程安全问题。
* 所以在getInstance方法上加入了同步。
* 虽然解决了安全问题,但是效率降低了,每一次要判断锁和是否为空。
* 可以通过双重判断的形式进行一下优化
**/
class Single{
private static Single s = null;
private Single(){}
public static synchronized Single getInstance(){
if(s==null)
s = new Single();
return s;
}
}
//经过优化的懒汉式
class Single2{
private static Single2 s = null;
private Single2(){}
public static Single2 getInstance(){
if(s==null){
synchronized (Single2.class) {
if(s==null)
s=new Single2();
}
}
return s;
}
}
//饿汉式
class ESingle{
private static final ESingle s = new ESingle();
private ESingle(){}
public static synchronized ESingle getInstance(){
return s;
}
}
同步的另一个弊端:死锁。通常发生在同步嵌套的情况下。
死锁例子:
class Demoo implements Runnable{
private boolean flag;
Demoo(boolean flag){
this.flag = flag;
}
public void run() {
if(flag){
synchronized (MyLock.lock1) {
System.out.println(Thread.currentThread().getName()+"if lock1");
synchronized (MyLock.lock2) {
System.out.println(Thread.currentThread().getName()+"iflock2");
}
}
}else{
synchronized (MyLock.lock2) {
System.out.println(Thread.currentThread().getName()+"elselock2");
synchronized (MyLock.lock1) {
System.out.println(Thread.currentThread().getName()+"elselock1");
}
}
}
}
}
class MyLock{
static MyLock lock1 = new MyLock();
static MyLock lock2 = new MyLock();
}
public class Thread7 {
public static void main(String[] args) {
Demoo d1 = new Demoo(true);
Demoo d2 = new Demoo(false);
new Thread(d1).start();
new Thread(d2).start();
}
}
线程间通信:
两个线程在同时处理同一个资源。而这两个线程的动作是不同的。
/*
* wait();notify();notifyAll();这三个方法都用于同步操作线程,为什么没有定义在Thread类中,而是定义在Object中?
* 因为这些方法需要标识出他们所在的锁。锁可以是任意对象。能被任意对象调用的方法定义在Object类中。
*/
class Resource {
String name;
String sex;
boolean boo;
public synchronized void set(String name,String sex){
if(boo)
try {wait();} catch (InterruptedException e) {}
this.name = name;
this.sex = sex;
boo = true;
notify();
}
public synchronized void out(){
if(!boo)
try {wait();} catch (InterruptedException e) {}
System.out.println(name+"...."+sex);
boo = false;
notify();
}
}
class Input implements Runnable {
private Resource resource;
Input(Resource resource) {
this.resource = resource;
}
public void run() {
int x = 0;
while (true) {
if (x == 0)
resource.set("潇洒哥", "男");
else
resource.set("凤姐", "女");
x = (x + 1) % 2;
}
}
}
class Output implements Runnable {
private Resource resource;
Output(Resource resource) {
this.resource = resource;
}
public void run() {
while (true)
resource.out();
}
}
public class Thread8 {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(new Input(resource)).start();
new Thread(new Output(resource)).start();
}
}
线程的stop方法过时了,如何停止线程呢?
有两种方法:
1. 定义标记,让run方法结束。(run方法中一般都定义循环,只要控制住循环条件即可)
但是这种方式有局限性,如果线程进入冻结状态,时不会读取标记的,这时不会停止。
2. 中断线程。其实就是清除线程的冻结状态,让线程恢复到可运行状态,这样就可以让线程去读取标记,并结束线程。
停止线程的原理就是让run方法结束。
wait();释放资源,释放锁。
sleep();释放资源,不释放锁。
释放资源的意思就是CPU不再处理该线程。